在 Oracle 19c RAC 環境中設計 TAF(Transparent Application Failover)實驗,主要關鍵在於Client 端必須使用基於 OCI(Oracle Call Interface)的連線工具(例如 Linux 下的 SQL*Plus 或 OCI 程式),因為 JDBC Thin 驅動程式並不支援 TAF。
以下是在 Linux Client 端透過 TNS 進行 TAF 驗證的完整實驗設計步驟。
一、 實驗前置準備
#
- 環境需求
- Database:Oracle 19c RAC 雙節點(假設節點為 node1、node2,Instance 名稱為
db1、db2,Service 名稱為 db175_svc)。
- Client OS:Linux,已安裝 Oracle Client 或 Oracle Instant Client(需包含 SQL*Plus 與相關 OCI 函式庫)。
- 確認 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:設定重試次數與重試間隔秒數。
- 測試 TNS 連通性
在 Linux Client 終端機執行:
確認能正常解析並連通監聽器。
- 在資料庫建立測試資料
使用 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 語句能自動在另一個節點繼續讀取資料。
- 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 顯示為 SELECT,FAILOVER_METHOD 為 BASIC,FAILED_OVER 顯示為 NO。
- Client 端發起長時間查詢
在該 SQL*Plus 視窗中執行大量資料查詢:
1
2
|
SET PAGESIZE 50000
SELECT * FROM test_taf;
|
- DB 端模擬節點故障
當 Client 螢幕正在大量輸出資料時,登入目前連線的那個 RAC 節點(例如 node1),以指令強制中止 Instance:
1
2
|
# 使用 srvctl 停止實例
srvctl stop instance -d db -i db1 -o abort -f
|
或者直接在該節點用 SQL*Plus 執行 shutdown abort;。
- 觀察 Client 端的現象
- 螢幕輸出會短暫停頓數秒(等待 OCI 偵測到連線中斷並重新向 node2 建立連線)。
- 隨後資料繼續捲動輸出,直到 100 萬筆資料全部撈取完畢,過程中不會跳出斷線錯誤。
- 確認切換後的 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 一律會被回滾並拋出例外。
- 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');
|
- DB 端再次將當前 Instance 關閉
1
|
srvctl stop instance -d db -i db1 -o abort -f
|
- 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 在查詢過程中的切換失敗現象。
- 修改 Client 端
tnsnames.ora 中的 FAILOVER_MODE 為 (TYPE = SESSION)。
- 重新連線 SQL*Plus,重複實驗 1 的流程(執行
SELECT * FROM test_taf; 時把 Instance shutdown abort)。
- 預期結果:
- 查詢會直接中斷並回傳錯誤訊息,例如
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>
|
三、 實務上的觀察與注意事項
#
- 切換延遲時間
在實際故障切換時,Client 會感受到幾秒鐘的卡頓。這是因為 TCP 連線中斷感知、逾時判定以及依據
RETRIES 和 DELAY 重試連線都需要時間。
- 應用程式相容性
TAF 是針對 OCI 開發的早期高可用技術。如果是使用標準 Java JDBC Thin Driver 的 Web 應用程式(如 Spring Boot、WebLogic、Tomcat),設定 TNS TAF 是完全無效的。這類應用在 RAC 環境中需搭配 UCP + FCF(Fast Connection Failover)。
參考資料
#