使用Data guard作為HA方案,要解決的一個問題在於:後台資料庫發生了主備切換,client串連如何做到自動切到新的primary資料庫上?
如果做通用的方案,需要用戶端自己提供自動重連的能力,這點大多數java的occi的串連池都有實現。
但這些已有實現大多是對同一串連配置發起重連,所以需要考慮為application提供透明的串連方式,而不讓應用看到具體data guard的多個ip和service name,這就需要做些額外的配置工作。
一種方式通過vip,真實轉寄的ip只掛靠在有效資料庫的ip上。這種方式切換髮生後,application在斷連的舊connection上發起dml會獲得ORA-3113 "end of file on communication channel"的錯誤,此時application可以嘗試重連機制和新的primary建立串連。
在f5上可以通過設定心跳sql和期望的返回結果內容,以類似ping方式擷取遠端資料庫是否可用,來決定ip是否應該轉寄到該物理ip上。
另一種方式是通過設定tns和資料庫的service name來訪問,通過合理設定,甚至可以做到在發生切換時的select操作僅僅被阻塞一會,而不會感覺到資料庫已經完成了主備切換。
設定步驟如下:
1.用戶端的tnsnames.ora中tns配置成
MYAPP =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = HostA)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = HostB)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = myapp)
)
)
2.在primary資料庫運行
begin
dbms_service.create_service("myapp","myapp");
end;
/
begin
DBMS_SERVICE.START_SERVICE("myapp");
end;
/
3.在primary資料庫建立觸發器:
create trigger myapptrigg after startup on database
declare
v_role varchar(30);
begin
select database_role into v_role from v$database;
if v_role = "PRIMARY" then
DBMS_SERVICE.START_SERVICE("myapp");
else
DBMS_SERVICE.STOP_SERVICE("myapp");
end if;
end;
/
解釋下:這個方案的思路就是將兩邊的資料庫的service name都設定成"myapp",當發生切換時,由觸發器在資料庫startup的時候把primary的執行個體以"myapp"的名字顯示,而把standby的"myapp"服務名給停掉,這樣任何時刻只有主節點顯示名字為"myapp"的服務。
注意這裡的plsql都是運行在primary,無需在standby上做任何設定,因為data guard會自動將變化同步到standby資料庫。
通過在primary資料庫運行下面程式,可以讓用戶端在做select的時候甚至意識不到資料庫的切換:
begin
dbms_service.modify_service
("myapp",
FAILOVER_METHOD => "BASIC",
FAILOVER_TYPE => "SELECT",
FAILOVER_RETRIES => 200,
FAILOVER_DELAY => 1);
end;
/
注意如果在切換時有comit的提交事務發生,還是會出現失誤提交失敗,要求復原的情況。
下面tns是另一種配置方式(類似rac的failover配置思想),使用這種方式,不需要在Oracle server中運行任何plsql指令碼,在DESCRIPTION_LIST中的兩個資料庫甚至根本不需要處於data guard中,可以是任意兩個資料庫。driver會按順序遍曆list中的資料庫,一直到能串連上為止。
MYAPP =
(DESCRIPTION_LIST=
(LOAD_BALANCE=off)
(FAILOVER=on)
(DESCRIPTION =(CONNECT_TIMEOUT=5)(TRANSPORT_CONNECT_TIMEOUT=3)(RETRY_COUNT=10)
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = myapp1)
)
)
(DESCRIPTION =(CONNECT_TIMEOUT=5)(TRANSPORT_CONNECT_TIMEOUT=3)(RETRY_COUNT=10)
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = otherIP)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = myapp2)
)
)
)
這種方式需要注意的地方:
1.jdbc必須走oci的方式,如果為jdbc:thin+tns方式,則會出現
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 545
at oracle.net.nl.NVTokens.parseTokens(Unknown Source)
at oracle.net.nl.NVFactory.createNVPair(Unknown Source)
其原因在於jdbc的driver本身無法識別這種格式的tns內容。
此時即使以jdbc:thin+tns的方式訪問其他正常的tns也會一樣拋出這個錯誤,因為這導致了jdbc根本無法正確解析整個tnsnames.ora檔案。
而jdbc:oci實際上負責解析tnsnames.ora和處理通訊的是依賴oci.lib,因此就不存在這個問題。
2.這種配置適用於任何依賴oci通訊的用戶端,包括oci,occi,一些基於它們的wrap庫,以及pl/sql developer此類的工具軟體。
3.注意如果串連的資料庫組屬於manually switch的模式,而不是fail down導致的切換,比如tns中的a資料庫是mount狀態,b是primary,而tns的列表順序是先a後b,則會出現儘管用戶端連a時,拋出ORA-0133錯誤,但是不會按順序去嘗試串連b。
原因是在處理這個連結時,oci用戶端會嘗試通過listener和service建立串連。
如果listener是關閉的,或者用戶端能連上listener但是找不到對應service,則都會嘗試串連處於第二個的b,但是如果通過listener找到了對端的service,只是無法建立串連(如資料庫處於mount狀態),則此時不會嘗試串連b,而直接會以拋出
ORA-0133:ORACLE initialization or shutdown in progress
終止串連嘗試。
所以在使用這種tns的時候要確保通過tns列表能訪問到的所有資料庫都不會一直處於mount狀態,否則串連它會打斷對後面正常open資料庫的串連嘗試。
這也是為何手動切換的dataguard資料庫,用戶端不能依賴這種tns配置方法做自動切換,因為手動切換的dataguard資料庫狀態肯定是一個open一個mount,如果mount處於tns的列表靠前的位置,在串連它失敗後會拋出ORA-0133異常阻止用戶端嘗試串連正常open的那個資料庫。
推薦閱讀:
RMAN 配置歸檔日誌刪除策略
Oracle基礎教程之通過RMAN複製資料庫
RMAN備份策略制定參考內容
RMAN備份學習筆記
OracleDatabase Backup加密 RMAN加密