快轉到主要內容

test-case-01 - OCI TAF Test

·2401 字·5 分鐘·
PolloChang
作者
PolloChang
我是一隻雞

在 Oracle 19c RAC 環境中設計 TAF(Transparent Application Failover)實驗,主要關鍵在於Client 端必須使用基於 OCI(Oracle Call Interface)的連線工具(例如 Linux 下的 SQL*Plus 或 OCI 程式),因為 JDBC Thin 驅動程式並不支援 TAF。

以下是在 Linux Client 端透過 TNS 進行 TAF 驗證的完整實驗設計步驟。


一、 實驗前置準備
#

  1. 環境需求
  • Database:Oracle 19c RAC 雙節點(假設節點為 node1、node2,Instance 名稱為 db1db2,Service 名稱為 db175_svc)。
  • Client OS:Linux,已安裝 Oracle Client 或 Oracle Instant Client(需包含 SQL*Plus 與相關 OCI 函式庫)。
  1. 確認 Client 端 TNS 設定 在 Linux Client 端的 $ORACLE_HOME/network/admin/tnsnames.ora(或 Instant Client 的 TNS 目錄)中加入以下 TAF 設定:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
RAC_TAF =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (LOAD_BALANCE = yes)
      (ADDRESS = (PROTOCOL = TCP)(HOST = vm171-db01-vip.home.pollochang.work)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = vm172-db02-vip.home.pollochang.work)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = db175_svc)
      (FAILOVER_MODE =
        (TYPE = SELECT)
        (METHOD = BASIC)
        (RETRIES = 180)
        (DELAY = 5)
      )
    )
  )
  • TYPE = SELECT:當節點中斷時,Session 會切換到另一節點,且未讀取完畢的 SELECT 查詢會繼續 fetch,不需手動重新執行。
  • METHOD = BASIC:在偵測到連線中斷時才向備用節點建立連線。
  • RETRIES / DELAY:設定重試次數與重試間隔秒數。
  1. 測試 TNS 連通性

在 Linux Client 終端機執行:

1
tnsping RAC_TAF

確認能正常解析並連通監聽器。

  1. 在資料庫建立測試資料 使用 DBA 帳號登入資料庫,建立一張資料量足夠大(例如 100 萬筆)的測試表,以便在執行查詢期間有足夠時間模擬節點故障:
1
2
3
4
CREATE TABLE test_taf AS
SELECT rownum AS id, 'TAF_TEST_DATA_' || rownum AS note, sysdate AS created_time
FROM dual
CONNECT BY level <= 1000000;

二、 實驗步驟
#

實驗 1:驗證 TYPE = SELECT 的無中斷查詢切換
#

目標:驗證當節點 Crash 時,正在進行的 SELECT 語句能自動在另一個節點繼續讀取資料。

  1. Client 端建立連線並查詢連線狀態 在 Linux Client 端開啟 SQL*Plus:
1
sqlplus pollo_ap/PaSsw0rd..@RAC_TAF

執行以下 SQL 確認當前連線的 Instance 與 TAF 註冊狀態:

1
2
3
4
5
SELECT instance_name, host_name FROM v$instance;

SELECT sid, serial#, failover_type, failover_method, failed_over 
FROM v$session 
WHERE sid = sys_context('userenv', 'sid');
1
2
3
4
5
6
SQL> SELECT sid, serial#, failover_type, failover_method, failed_over 
FROM v$session WHERE sid = sys_context('userenv', 'sid');  2  

       SID    SERIAL# FAILOVER_TYPE FAILOVER_M FAI
---------- ---------- ------------- ---------- ---
       751      15563 SELECT        BASIC      NO

一般使用者可以投或下列方式查詢當前的資料庫連線所在的 instance。

1
2
3
4
5
6
SELECT 
    sys_context('userenv', 'instance_name') AS instance_name,
    sys_context('userenv', 'server_host')   AS host_name,
    sys_context('userenv', 'service_name')  AS service_name,
    sys_context('userenv', 'sid')           AS sid
FROM dual;

以下是一般使用的連線查詢結果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
SQL> SELECT                                          
    sys_context('userenv', 'instance_name') AS instance_name,
    sys_context('userenv', 'server_host')   AS host_name,
    sys_context('userenv', 'service_name')  AS service_name,
    sys_context('userenv', 'sid')           AS sid
FROM dual;  2    3    4    5    6  

INSTANCE_NAME
--------------------------------------------------------------------------------
HOST_NAME
--------------------------------------------------------------------------------
SERVICE_NAME
--------------------------------------------------------------------------------
SID
--------------------------------------------------------------------------------
db1
vm171-db01
db175_svc
746

預期結果FAILOVER_TYPE 顯示為 SELECTFAILOVER_METHODBASICFAILED_OVER 顯示為 NO

  1. Client 端發起長時間查詢 在該 SQL*Plus 視窗中執行大量資料查詢:
1
2
SET PAGESIZE 50000
SELECT * FROM test_taf;
  1. DB 端模擬節點故障 當 Client 螢幕正在大量輸出資料時,登入目前連線的那個 RAC 節點(例如 node1),以指令強制中止 Instance:
1
2
# 使用 srvctl 停止實例
srvctl stop instance -d db -i db1 -o abort -f

或者直接在該節點用 SQL*Plus 執行 shutdown abort;

  1. 觀察 Client 端的現象
  • 螢幕輸出會短暫停頓數秒(等待 OCI 偵測到連線中斷並重新向 node2 建立連線)。
  • 隨後資料繼續捲動輸出,直到 100 萬筆資料全部撈取完畢,過程中不會跳出斷線錯誤。
  1. 確認切換後的 Session 狀態 查詢結束後,在同一個 SQL*Plus 視窗中再次執行:
1
2
3
SELECT instance_name, host_name FROM v$instance;

SELECT sid, serial#, failover_type, failover_method, failed_over FROM v$session WHERE sid = sys_context('userenv', 'sid');
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
SQL> SELECT 
    sys_context('userenv', 'instance_name') AS instance_name,
    sys_context('userenv', 'server_host')   AS host_name,
    sys_context('userenv', 'service_name')  AS service_name,
    sys_context('userenv', 'sid')           AS sid
FROM dual;  2    3    4    5    6  

INSTANCE_NAME
--------------------------------------------------------------------------------
HOST_NAME
--------------------------------------------------------------------------------
SERVICE_NAME
--------------------------------------------------------------------------------
SID
--------------------------------------------------------------------------------
db2
vm172-db02
db175_svc
751

SQL> SELECT sid, serial#, failover_type, failover_method, failed_over 
FROM v$session WHERE sid = sys_context('userenv', 'sid');  2  

       SID    SERIAL# FAILOVER_TYPE FAILOVER_M FAI
---------- ---------- ------------- ---------- ---
       751      15563 SELECT        BASIC      YES

預期結果INSTANCE_NAME 已變成 db2,且 FAILED_OVER 變成 YES


實驗 2:驗證未提交交易(DML)的自動 Rollback
#

目標:確認 TAF 無法重放寫入交易,未提交的 Transaction 一律會被回滾並拋出例外。

  1. Client 端開啟連線並執行未提交的 DML 在 Linux Client 端重新以 SQL*Plus 連線:
1
sqlplus pollo_ap/PaSsw0rd..@RAC_TAF
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
SQL> SELECT instance_name, host_name FROM v$instance;

INSTANCE_NAME
----------------
HOST_NAME
----------------------------------------------------------------
db1
vm171-db01.home.pollochang.work


SQL> SELECT sid, serial#, failover_type, failover_method, failed_over 
FROM v$session WHERE sid = sys_context('userenv', 'sid');  2  

       SID    SERIAL# FAILOVER_TYPE FAILOVER_M FAI
---------- ---------- ------------- ---------- ---
       139      15645 SELECT        BASIC      NO

執行 INSERT 但不要下 COMMIT:

1
INSERT INTO test_taf (id, note) VALUES (9999999, 'UNCOMMITTED_DATA');
  1. DB 端再次將當前 Instance 關閉
1
srvctl stop instance -d db -i db1 -o abort -f
  1. Client 端發送下一個指令 在 Client 端嘗試執行任意 SQL(例如 COMMIT;SELECT * FROM test_taf WHERE id = 9999999;): 預期結果
  • Client 會收到錯誤訊息,通常為 ORA-25402: transaction must roll back
  • Session 雖然重新連接到了健康的節點,但剛才未提交的 INSERT 已經被 Rollback。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
SQL> INSERT INTO test_taf (id, note) VALUES (9999999, 'UNCOMMITTED_DATA');

1 row created.

SQL> SELECT * FROM test_taf WHERE id = 9999999;
SELECT * FROM test_taf WHERE id = 9999999
            *
ERROR at line 1:
ORA-25402: transaction must roll back

SQL> rollback;

Rollback complete.

SQL> seLECT sid, serial#, failover_type, failover_method, failed_over FROM v$session WHERE sid = sys_context('userenv', 'sid');

       SID    SERIAL# FAILOVER_TYPE FAILOVER_M FAI
---------- ---------- ------------- ---------- ---
       751      32978 SELECT        BASIC      YES

SQL> 

實驗 3:對比 TYPE = SESSION 的行為差異
#

目標:觀察 TYPE = SESSION 在查詢過程中的切換失敗現象。

  1. 修改 Client 端 tnsnames.ora 中的 FAILOVER_MODE(TYPE = SESSION)
  2. 重新連線 SQL*Plus,重複實驗 1 的流程(執行 SELECT * FROM test_taf; 時把 Instance shutdown abort)。
  3. 預期結果
  • 查詢會直接中斷並回傳錯誤訊息,例如 ORA-25401: can not continue fetches
  • 雖然連線本體(Session)已遷移至新節點,但 Cursor 無法繼續 Fetch,必須重新手動執行查詢語句。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
NO_RAC_TAF =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (LOAD_BALANCE = yes)
      (ADDRESS = (PROTOCOL = TCP)(HOST = vm171-db01-vip.home.pollochang.work)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = vm172-db02-vip.home.pollochang.work)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = db175_svc)
      (FAILOVER_MODE =
        (TYPE = SESSION)
        (METHOD = BASIC)
        (RETRIES = 180)
        (DELAY = 5)
      )
    )
  )
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 sqlplus pollo_ap/PaSsw0rd..@NO_RAC_TAF 

SQL> SELECT instance_name, host_name FROM v$instance;

INSTANCE_NAME
----------------
HOST_NAME
----------------------------------------------------------------
db2
vm172-db02.home.pollochang.work


SQL> SELECT sid, serial#, failover_type, failover_method, failed_over FROM v$session WHERE sid = sys_context('userenv', 'sid');

       SID    SERIAL# FAILOVER_TYPE FAILOVER_M FAI
---------- ---------- ------------- ---------- ---
       629      25203 SESSION       BASIC      NO

SQL> SELECT * FROM test_taf;
    1 TAF_TEST_DATA_999995                                   19-AUG-26
    ...
        ID NOTE                                                   CREATED_T
---------- ------------------------------------------------------ ---------
     19119 TAF_TEST_DATA_19119                                    19-AUG-26
     19120 TAF_TEST_DATA_19120                                    19-AUG-26
     19121 TAF_TEST_DATA_19121                                    19-AUG-26
     19122 TAF_TEST_DATA_19122                                    19-AUG-26
     19123 TAF_TEST_DATA_19123                                    19-AUG-26
     19124 TAF_TEST_DATA_19124                                    19-AUG-26
     19125 TAF_TEST_DATA_19125                                    19-AUG-26
ERROR:
ORA-25401: can not continue fetches



19125 rows selected.

SQL> 

SQL> SELECT instance_name, host_name FROM v$instance;

INSTANCE_NAME
----------------
HOST_NAME
----------------------------------------------------------------
db1
vm171-db01.home.pollochang.work


SQL> SELECT sid, serial#, failover_type, failover_method, failed_over FROM v$session WHERE sid = sys_context('userenv', 'sid');

       SID    SERIAL# FAILOVER_TYPE FAILOVER_M FAI
---------- ---------- ------------- ---------- ---
       862      57532 SESSION       BASIC      YES

SQL> 

三、 實務上的觀察與注意事項
#

  1. 切換延遲時間 在實際故障切換時,Client 會感受到幾秒鐘的卡頓。這是因為 TCP 連線中斷感知、逾時判定以及依據 RETRIESDELAY 重試連線都需要時間。
  2. 應用程式相容性 TAF 是針對 OCI 開發的早期高可用技術。如果是使用標準 Java JDBC Thin Driver 的 Web 應用程式(如 Spring Boot、WebLogic、Tomcat),設定 TNS TAF 是完全無效的。這類應用在 RAC 環境中需搭配 UCP + FCF(Fast Connection Failover)。

參考資料
#