文章轉自:http://www.oracle.com/technetwork/cn/articles/database-performance/oracle-rac-connection-mgmt-1650424-zhs.htmlhtml
做者:崔華node
這篇文章詳細介紹了Oracle RAC環境下的鏈接管理,分別介紹了什麼是 Connect Time Load Balancing、Runtime Connection Load Balancing、Connect Time Connection Failover 和 Runtime Connection Failover,以及裏面所涉及到的 TAF、ONS、FCF、FAN、LBA 等諸多知識點。本文主要是針對 Oracle RAC 11gR2 環境下的鏈接管理,但同時也會對比說明一下 Oracle RAC 10gR2/9iR2,以體現他們之間在鏈接管理上的差別。sql
所謂「鏈接管理」,主要體如今 Load Balancing 和 Failover 兩方面。Oracle RAC 11gR2 下的 Load Balancing 和 Failover,根據是否使用了事先已經存在的鏈接(如鏈接池中的鏈接)又分爲 Connect Time Load Balancing、Runtime Connection Load Balancing、Connect Time Connection Failover和Runtime Connection Failover 這 4 種類型,凡是帶上了「Runtime」前綴的,就是指鏈接已經存在的狀況,好比使用了鏈接池。數據庫
Connect Time Connection Failover 是指不從鏈接池中取得已有鏈接,而是直接鏈接 Oracle 數據庫時的 Failover。在 Oracle RAC 11gR2 以前,Connect Time Connection Failover 是很是容易實現的,只須要在相關的 tnsnames.ora 中指定多個 vip,同時指定 FAILOVER=ON 就行了。以下所示:session
(DESCRIPTION= (FAILOVER=ON) (ADDRESS_LIST= (LOAD_BALANCE=OFF) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC1-vip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC2-vip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC3-vip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC4-vip)(PORT=1521)) ) (CONNECT_DATA=(SERVICE_NAME=RAC10g)) )
這裏客戶端進程首先會嘗試鏈接 RAC1-vip,若是連不上,則會嘗試RAC2-vip,再連不上,則會繼續往下嘗試,直到全部出如今 ADDRESS_LIST 中的 vip 地址所有順序嘗試完爲止。這種客戶端在鏈接 Oracle 數據庫時的 Failover,不只適用於 RAC 環境,也適用於 Data Guard 環境。以下所示:架構
DESCRIPTION= (FAILOVER=ON) (ADDRESS_LIST= (LOAD_BALANCE=OFF) (ADDRESS=(PROTOCOL=TCP)(HOST=primary-ip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=standby-ip)(PORT=1521)) ) (CONNECT_DATA=(SERVICE_NAME=service10g)) )
Oracle RAC 11gR2 引入了 SCAN(Single Client Access Name),而且客戶端缺省是經過 SCAN 來鏈接整個 RAC 環境的,以下是 SCAN 的架構圖:併發
如上圖所示,若是使用了 DNS 或者 GNS (Grid Naming Service),那麼最多能夠有 3 個 SCAN VIP 和 3 個 SCAN Listener;若是沒有使用 DNS 或者 GNS,而是選擇使用 hosts 文件,則只會有 1 個 SCAN VIP 和 1 個 SCAN Listener。oracle
這裏假設在 tnsnames.ora 中這樣配置:app
(DESCRIPTION = (FAILOVER=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = MySCAN)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME =RAC11g)))
嚴格意義上說,只有在 RAC 環境有 1 個以上 SCAN VIP 的時候,上述 FAILOVER=ON 纔有意義——它表示的是客戶端在鏈接 SCAN VIP 的時候,若是其中的一個 SCAN VIP 連不上,則立刻會嘗試另一個 SCAN VIP。ide
當使用了 hosts 文件來指定 SCAN VIP 的時候,即在整個 RAC 環境只有 1 個 SCAN VIP 的狀況下,Failover 其實也存在,只不過這種狀況下 Failover 的速度會慢一些。由於當 SCAN VIP 所在的節點宕掉後,SCAN VIP 會和相關的 SCAN Listener 一塊兒總體 Failover 到其餘節點,只不過這個 Failover 須要時間,而客戶端須要等待這個 Failover 過程完畢後才能從新連上 RAC。
Runtime Connection Failover 是指鏈接已經存在的狀況下的 Failover。這個已存在的鏈接,多是鏈接池中正在用的鏈接,也多是不經過鏈接池、直接經過 OCI 客戶端(如 sqlplus)連上 Oracle 數據庫後的鏈接。
這種 Runtime Connection Failover,就是指在鏈接已經存在的狀況下,若是 Oracle 數據庫端出現了異常的狀況(好比 Service 宕了、Instance 崩潰了、Session 斷了)而致使已有鏈接中斷,怎樣 Failover 的問題。
有兩種手段來實現 Runtime Connection Failover,分別爲 TAF(Transparent Application Failover)和 FCF(Fast Connection Failover)。
首先來介紹 TAF。TAF 有以下一些知識點須要咱們注意:
一、它能夠在 client 端的 tnsnames.ora 中的鏈接串裏定義,也能夠在 server 端的 service 中定義,只不過 service 端的設置會取代(override)客戶端 tnsnames.ora 中的設置:
客戶端能夠這樣設置 TAF:
(DESCRIPTION = (FAILOVER=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = MySCAN)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = Email) (FAILOVER_MODE= (TYPE=select)(METHOD=basic)(RETRIES=180)(DELAY=5)))
Server 端能夠這樣設置 TAF:
srvctl modify service -d RAC11g -s Email -q TRUE -P BASIC -e SELECT -z 180 -w 5 -j LONG 具體各個參數的含義可參見以下注釋:
Usage: srvctl modify service -d <db_unique_name> -s <service_name> [-c {UNIFORM | SINGLETON}] [-P {BASIC|PRECONNECT|NONE}] [-l [PRIMARY][,PHYSICAL_STANDBY][,LOGICAL_STANDBY][,SNAPSHOT_STANDBY]] [-y {AUTOMATIC | MANUAL}][-q {true|false}] [-x {true|false}] [-j {SHORT|LONG}] [-B {NONE|SERVICE_TIME|THROUGHPUT}] [-e {NONE|SESSION|SELECT}] [-m {NONE|BASIC}] [-z <integer>] [-w <integer>] -d <db_unique_name> Unique name for the database -s <service> Service name -c {UNIFORM | SINGLETON} Service runs on every active server in the server pool hosting this service (UNIFORM) or just one server (SINGLETON) -P {NONE | BASIC | PRECONNECT} TAF policy specification -l <role> Role of the service (primary, physical_standby, logical_standby, snapshot_standby) -y <policy> Management policy for the service (AUTOMATIC or MANUAL) -e <Failover type> Failover type (NONE, SESSION, or SELECT) -m <Failover method> Failover method (NONE or BASIC) -w <integer> Failover delay -z <integer> Failover retries -j <clb_goal> Connection Load Balancing Goal (SHORT or LONG). Default is LONG. -B <Runtime Load Balancing Goal> Runtime Load Balancing Goal (SERVICE_TIME, THROUGHPUT, or NONE) -x <Distributed Transaction Processing> Distributed Transaction Processing (TRUE or FALSE) -q <AQ HA notifications> AQ HA notifications (TRUE or FALSE) -h Print usage
二、當 TAF 的TYPE 設置爲 select 的時候,單純 select 操做(不包括 select … for update)能夠作到「斷點續傳」,即單純的 select 操做在利用 TAF 實現 Failover 後是能夠從中斷的地方繼續往下執行的;
三、TAF 對 DML 操做不能作到「斷點續傳」,即若是一個 transaction 在使用 TAF 實現 Failover 後,該 transaction 不能從中斷的地方繼續執行,須要再次從頭開始執行;
四、TAF 僅對使用 OCI 鏈接的客戶端和鏈接池有效,這裏的 OCI 鏈接能夠是在 OCI 鏈接上的封裝,好比 JDBC-OCI driver 就支持 TAF,但 JDBC thin driver 就不支持 TAF(由於 JDBC thin driver 不是基於 OCI 的)。
接下來,在介紹 FCF(Fast Connection Failover)以前,咱們必需要先介紹 FAN(Fast Application Notification)。
FAN 是 Oracle RAC 裏的一種消息主動通知機制。當 RAC 裏出現 service down/up,instance down/up,節點負載變化時,Oracle 數據庫都能經過 FAN events 將這些信息發佈出去,訂閱這些 FAN events 的客戶端在第一時間收到這些 FAN events 後就能作出相應的動做來響應這些 FAN events。
FAN events 分爲兩種,第一種是 FAN HA events,第二種是 LBA events,這裏的 LBA 是指 Load Balancing Advisory。
當 RAC 裏出現 service down/up、instance down/up 時就會觸發 FAN HA events。FAN HA events 的示例以下所示:
Event 1: FAN event type: instance Properties: version=1.0 service=PROD database=PROD instance=PROD1 host=node1 status=down Event 2: FAN event type: service_member Properties: version=1.0 service=ERP database=PROD instance=PROD1 host=node1 status=down Event 3: FAN event type: service_member Properties: version=1.0 service=ERP database=PROD instance=PROD3 host=node3 status=up
RAC 裏節點的負載變化後也會產生 LBA events,LBA events 的示例以下所示:
Event 4: FAN-event type: service_metrics Properties: version=2.0 service=ERP database=PROD instance=PROD1 percent=70 service_quality=GOOD instance=PROD2 percent=30 service_quality=GOOD Event 5 :FAN-event type: service_metrics Properties: version=2.0 service=CRM database=PROD instance=PROD2 percent=30 service_quality=GOOD instance=PROD3 percent=70 service_quality=GOOD
上述 FAN events 可能會經過多種渠道傳播出去,這些渠道包括 ONS(Oracle Notification Service),AQ(Advanced Queue),PMON 等。下面是關於 FAN events 架構和傳播途徑的兩張圖,它們就直觀的說明了 FAN events 的傳播途徑:
訂閱 FAN HA events 的客戶端包括:JDBC Implicit Connection Cache, OCI, ODP.NET Connection Pools, Listener, Server Side Callouts 等;
訂閱 LBA events 的客戶端包括:JDBC Implicit Connection Cache, ODP.NET Connection Pools, Listener,OCI Session Pools 等;
介紹完 FAN,如今能夠開始介紹 FCF:FCF 的意思是 Fast Connection Failover,它其實是客戶端經過訂閱 FAN HA events 來實現的。以下是兩個客戶端經過訂閱 FAN HA events 來實現 FCF 的例子:
例一:JDBC Fast Connection Failover (FCF)
這裏的 JDBC 鏈接是指 JDBC thin 鏈接。由於 JDBC thin 鏈接不是基於 OCI 的,因此這種狀況下的 Runtime Connection Failover 不能使用 TAF,只能用 FCF。而且要作以下幾件事情後才能夠正常使用 FCF:
一、把 implicit connection cache 打開;
二、把 FastConnectionFailoverEnabled 打開;
三、最好是直接訂閱遠程的 ONS(在Oracle 10gR2 以前的版本不能直接訂閱遠程的 ONS,只能經過在本地安裝 ONS 後來實現 FAN events 的中轉);
四、最好是在 Java 程序裏設置一下 TCP timeout(後面專門會講到在 Oracle 數據庫裏如何調整 TCP timeout);
演示代碼以下:
OracleDataSource ods = new OracleDataSource() ... ods.setUser(「Scott」) ods.setPassword(「tiger」) ods.setConnectionCachingEnabled(true); ods.setFastConnectionFailoverEnabled(true); ods.setConnectionCacheName(「MyCache」) ods.setConnectionCacheProperties(cp); ods.setONSConfiguration("nodes=racnode1:6201,racnode2.:6201"); ods.setURL("jdbc:oracle:thin:@sales1-scan:1521/oltp"); //TCP connect timeout Properties prop = new Properties(); prop.setProperty("MinLimit", MIN_CONN); prop.setProperty("MaxLimit", MAX_CONN); prop.setProperty("InitialLimit", INIT_CONN); prop.put (oracle.net.ns.SQLnetDef.TCP_CONNTIMEOUT_STR, "1000")); // 這裏是表示把TCP timeout設爲1000毫秒,即1秒 ods.setConnectionCacheProperties(prop);
例二:ODP.NET Fast Connection Failover (FCF)
對於 ODP.NET 而言,一般作了以下幾件事情後就可使用 FCF 了:
一、把對應 service 的 AQ Notification 打開:
srvctl modify service -d RAC11g -s Email -q TRUE
二、把 aq_tm_processes 的值設爲 1;
三、賦予指定用戶 de-queue 的權限:
exec dbms_aqadm.grant_queue_privilege('DEQUEUE','SYS.SYS$SERVICE_METRICS', <your username=>);
四、在 .NET 鏈接串裏設置 HA events=true;
演示代碼以下:
// C# using System; using Oracle.DataAccess.Client; class ConnectionPoolingSample { static void Main() { OracleConnection con = new OracleConnection(); //Open a connection using ConnectionString attributes //related to connection pooling. con.ConnectionString = "User Id=scott;Password=tiger;Data Source=crm;" + "Min Pool Size=10;Connection Lifetime=120;Connection Timeout=60;" + "HA events=true", "Incr Pool Size=5; Decr Pool Si=2"; con.Open(); Console.WriteLine("Connection pool successfully created"); // Close and Dispose OracleConnection object con.Close(); con.Dispose(); Console.WriteLine("Connection is placed back into the pool."); } }
FCF 跟 TAF 有一個很大的不一樣就是即使是單純 select 操做,FCF 也不能像 TAF 那樣作到「斷點續傳」。對於配置好了 FCF 的鏈接池而言,當它接收到包含 instance/service 宕掉的 FAN HA events 後,原先 cache 在鏈接池裏的跟這個 instance/service 相關的鏈接立刻會被標記爲失效(invalid)同時這些鏈接會被清除,使用這些鏈接的 transaction 也會立刻停止並回滾。當應用捕捉到這個停止的 transaction 所產生的錯誤信息後,要麼直接把相關錯誤返回給最終用戶,要麼從鏈接池中從新取得一個有效鏈接並從新執行這個被停止的 transaction。
在啓用了 FCF 的狀況下,若是鏈接錯誤被返回給了最終用戶,那麼應該如何判斷錯誤信息的來源呢(便是否是 FCF 返回的錯誤)?很簡單,用 isFatalConnectionError(SQLException e)來判斷一下就行了,演示代碼以下:
try { conn = getConnection(); //這裏取得鏈接後作相關的工做 } catch (SQLException e) { handleSQLException(e) } ... void handleSQLException (SQLException e) { if (OracleConnectionCacheManager.isFatalConnectionError(e)) ConnRetry = true; //這裏表示捕捉到FCF返回的錯誤 … }
Connect Time Load Balancing 就是指不從鏈接池中取得已有鏈接,而是直接鏈接 Oracle 數據庫時的 Load Balance。Connect Time Load Balancing 又細分爲兩種,分別是客戶端的 Connect Time Load Balancing 和 Server 端的 Connect Time Load Balancing。
在 Oracle RAC 11gR2 以前,客戶端的 Connect Time Load Balancing 很是容易實現,只須要在相關的 tnsnames.ora 中指定多個 vip,同時指定 LOAD_BALANCE=ON 就行了。以下所示:
(DESCRIPTION= (ADDRESS_LIST= (LOAD_BALANCE=ON) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC1-vip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC2-vip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC3-vip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC4-vip)(PORT=1521)) ) (CONNECT_DATA=(SERVICE_NAME=RAC10g)) )
這樣客戶端在鏈接的時候,會隨機地從上述 4 個 VIP 地址中選一個來鏈接 Oracle 數據庫以達到 Load Balance 的目的。
以前已經提到,Oracle RAC 11gR2 中引入了 SCAN(Single Client Access Name),而且客戶端缺省是經過 SCAN 來鏈接整個 RAC 環境的,若是使用了 DNS 或者 GNS (Grid Naming Service),那麼最多能夠有 3 個 SCAN VIP 和 3 個 SCAN Listener;若是沒有使用 DNS 或者 GNS,而是選擇使用 hosts 文件,則只會有 1 個 SCAN VIP 和 1 個 SCAN Listener。
這裏假設你在 tnsnames.ora 中這樣配置:
(DESCRIPTION = (LOAD_BALANCE=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = MySCAN)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME =RAC11g)))
嚴格意義上說,只有在 RAC 環境有 1 個以上 SCAN VIP 的時候,上述 LOAD_BALANCE=ON 纔有意義——它表示的是客戶端在鏈接 Oracle 11gR2 RAC 的時候,會隨機的選擇三個 SCAN VIP 中的一個來鏈接,因此 Oracle 11gR2 RAC 的客戶端 Connect Time Load Balancing 其實是針對 SCAN VIP 而言的,而不是像 Oracle RAC 10gR2 /9iR2 那樣直接針對 RAC 節點的 VIP(Oracle RAC 9iR2 裏沒有 VIP,此時 Connect Time Load Balancing 是針對 public ip)。
當使用了 hosts 文件來指定 SCAN VIP 的時候,客戶端 Connect Time Load Balancing 其實是不存在的,由於如今整個 RAC 環境只有 1 個 SCAN VIP。
如今再來介紹一下 Server 端的 Connect Time Load Balancing。Server 端的 Connect Time Load Balancing 相對來講要複雜一些,下面針對 Oracle 數據庫不一樣的版原本分別加以說明。
首先要說明的是:不管是 Oracle RAC 9iR2/10gR2,仍是 Oracle RAC 11gR2,它們的 Server 端 Connect Time Load Balancing 都是經過聯合使用 local_listener 和 remote_listener 來實現的。
先來介紹 Oracle RAC 9iR2 下的 Server 端 Connect Time Load Balancing:
這裏假設是一個 4 節點的 Oracle RAC 9iR2,tnsnames.ora 中的鏈接串是以下這樣:
(DESCRIPTION= (FAILOVER=ON) (ADDRESS_LIST= (LOAD_BALANCE=ON) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC1-ip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC2-ip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC3-ip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC4-ip)(PORT=1521)) ) (CONNECT_DATA=(SERVICE_NAME=RAC9i)) )
再在各個節點的 tnsnames.ora 中加入以下設置:
LISTENER_RAC1 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC1-ip)(PORT = 1521)) LISTENER_RAC2 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC2-ip)(PORT = 1521)) LISTENER_RAC3 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC3-ip)(PORT = 1521)) LISTENER_RAC4 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC4-ip)(PORT = 1521)) LISTENERS_RAC = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC1-ip)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = RAC2-ip)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = RAC3-ip)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = RAC4-ip)(PORT = 1521)) )
而後只須要在初始化參數文件里加入以下設置就能實現 Server 端 Connect Time Load Balancing 了:
RAC1.local_listener=LISTENER_RAC1 RAC2.local_listener=LISTENER_RAC2 RAC3.local_listener=LISTENER_RAC3 RAC4.local_listener=LISTENER_RAC4 *.remote_listener=LISTENERS_RAC
當作了上述設置後,上述 4 個節點的 listener 實際上除了知道本節點負載的狀況以外,同時也知道了其他節點的負載狀況。因此當先通過一層客戶端的 Connect Time Load Balancing,好比這裏隨機地連到了第二個節點上的 listener(即 LISTENER_RAC2)上,當 LISTENER_RAC2 發現自身的負載較高,是有可能把你的鏈接請求轉移(redirect)到其他負載較低的節點的 listener 上的——這就是所謂的第二層 Load Balancing,也就是 server 端的 Connect Time Load Balancing。
Oracle RAC 9iR2 裏 server 端的 Connect Time Load Balancing 的依據是各節點 CPU 的負載(CPU runqueue-based load)或各節點所鏈接的 session 的數量。咱們能夠在相應節點的 listener.ora 中經過參數 prefer_least_loaded_node_<LISTENER_NAME>來控制 Oracle RAC 9iR2 數據庫到底採用哪一種判斷依據。prefer_least_loaded_node_ <LISTENER_NAME>的默認值是 on,意味着 Listener 會把鏈接轉移(redirect)到 CPU 負載較低的節點,即這種狀況下判斷負載的依據是各節點 CPU 的負載狀況;若是把它設爲 off,則意味着 Listener 在轉移(redirect)鏈接的時候會考慮各個節點已鏈接 session 的數量而且會盡可能保證各個節點所鏈接 session 數量的均衡,即這種狀況下判斷負載的依據是各節點已鏈接 session 的數量。
接着再來看 Oracle RAC 10gR2 下的 Server 端 Connect Time Load Balancing。Oracle RAC 10gR2 裏 Server 端 Connect Time Load Balancing 也是經過聯合使用 local_listener 和 remote_listener 來實現的,只不過 Oracle RAC 10gR2 裏引入了 VIP,因此這裏 local_listener 和 remote_listener 必定是要監聽 VIP,而不是像 Oracle RAC 9iR2 那樣監聽 public ip 了。
這裏假設是一個 4 節點的 Oracle RAC 10gR2,tnsnames.ora 中的鏈接串是以下所示(注意這裏的鏈接地址已是 VIP 了):
(DESCRIPTION= (FAILOVER=ON) (ADDRESS_LIST= (LOAD_BALANCE=ON) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC1-vip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC2-vip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC3-vip)(PORT=1521)) (ADDRESS=(PROTOCOL=TCP)(HOST=RAC4-vip)(PORT=1521)) ) (CONNECT_DATA=(SERVICE_NAME=RAC10g)) )
再在各個節點的 tnsnames.ora 中加入以下設置(注意這裏監聽的已是 VIP了):
LISTENER_RAC1 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC1-vip)(PORT = 1521)) LISTENER_RAC2 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC2-vip)(PORT = 1521)) LISTENER_RAC3 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC3-vip)(PORT = 1521)) LISTENER_RAC4 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC4-vip)(PORT = 1521)) LISTENERS_RAC = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC1-vip)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = RAC2-vip)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = RAC3-vip)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = RAC4-vip)(PORT = 1521)) )
而後只須要在初始化參數文件里加入以下設置就能實現 Server 端 Connect Time Load Balancing 了:
RAC1.local_listener=LISTENER_RAC1 RAC2.local_listener=LISTENER_RAC2 RAC3.local_listener=LISTENER_RAC3 RAC4.local_listener=LISTENER_RAC4 *.remote_listener=LISTENERS_RAC
當作了上述設置後,與 Oracle RAC 9iR2 同樣,也能夠實現 Server 端 Connect Time Load Balancing。只不過這裏判斷負載的依據有了變化。(注:若是客戶端不能解析 RAC1-vip 這樣的主機名,則在鏈接時極可能報 ORA-12545 錯誤,local_listener 對應的 TNS 配置中應該使用 VIP 地址,而不要用主機名,這裏寫主機名只是出於演示的目的)
Oracle 10g 引入了 Service,因此在 Oracle 10g 裏,判斷負載的依據就跟 Service 綁定在了一塊兒。Oracle RAC 10gR2 裏 Server 端 Connect Time Load Balancing 判斷負載的依據是由相關 service 的參數 CLB_GOAL 和 GOAL 聯合來決定的。
Oracle RAC 10gR2 裏的負載能夠經過 v$servicemetric 來查看:
SQL> desc v$servicemetric; Name Type Nullable Default Comments ----------------- ------------ -------- ------- -------- BEGIN_TIME DATE Y END_TIME DATE Y INTSIZE_CSEC NUMBER Y GROUP_ID NUMBER Y SERVICE_NAME_HASH NUMBER Y SERVICE_NAME VARCHAR2(64) Y CTMHASH NUMBER Y ELAPSEDPERCALL NUMBER Y CPUPERCALL NUMBER Y DBTIMEPERCALL NUMBER Y CALLSPERSEC NUMBER Y DBTIMEPERSEC NUMBER Y GOODNESS NUMBER Y DELTA NUMBER Y FLAGS NUMBER Y
其中每一個 service 在 v$servicemetric 裏會對應兩條記錄,一條記錄每5秒採樣一次,另一條記錄每 60 秒採樣一次。
這裏衡量每一個 service 的負載狀況,主要是經過 GOODNESS、DELTA和FLAGS 這三列來講明的,以下是它們各自的含義:
GOODNESS 表示這個節點成爲 Server 端 Connect Time Load Balancing 的目標節點的可能性,這個值越高,可能性就越低。即這個 service 在某個節點上的 GOODNESS 的值越大,則代表這個節點的負載越重,這個節點成爲 Server 端 Connect Time Load Balancing 的目標節點的可能性就越低。
DELTA 表示當節點增長了一個額外的 session 後對負載增長狀況的估算。
FLAGS 是一個標誌位,它的各個值的含義以下:
0 – all good 1 – blocked 2 – crossed threshold 4 – goodness unknown (usually when no sessions connected)
每一個 service 所對應的 CLB_GOAL 實際上表示 Client Load Balance Goal,它的值要麼爲 LONG,要麼爲 SHORT,默認值是 LONG。
LONG 和 SHORT 的區別是:LONG 是 CLB_GOAL 的缺省值,一般用於那些須要長時間保持的鏈接,好比一些第三方的鏈接池或者 SQL*Form 應用;而 SHORT 則一般用於那些鏈接持續時間較短的應用,若是使用了支持訂閱 LBA(Load Balancing Advisory)的鏈接池,則應該把 CLB_GOAL 的值設爲 SHORT。
若是一個 service 的 CLB_GOAL 被設爲 LONG,則意味着衡量這個 service 所在節點的負載狀況是依據鏈接到這個節點的 session 的數量,此時與 CLB_GOAL 相對應的另一個參數 GOAL 的設置將再也不生效。
若是你把一個 service 的 CLB_GOAL 設爲 SHORT,則意味着衡量這個 service 的負載狀況是依據 LBA,在根據 LBA 判斷負載狀況時根據對應 service 的 GOAL 的設置的值的不一樣,又能夠細分爲是依據 SERVICE_TIME 仍是依據 THROUGHPUT。也就是說,每一個 service 所對應的 GOAL 實際上表示 LBA GOAL,它的值要麼爲 THROUGHPUT,要麼爲 SERVICE_TIME,要麼是 NONE,GOAL 的默認值是 NONE。即當你把 CLB_GOAL 設爲 SHORT 後,這種狀況下 Server 端 Connect Time Load Balancing 判斷負載的依據就是由 GOAL 的設置來決定了。
GOAL 所對應的三個值 THROUGHPUT、SERVICE_TIME 和 NONE 的區別是:
THROUGHPUT:表示判斷負載的依據是吞吐量(THROUGHPUT),這一般用於那些併發的 transaction 具備類似的完成時間、類似的完成速率的系統,好比在線交易系統;
SERVICE_TIME:表示判斷負載的依據是響應時間(response time),這一般用於那些併發的 transaction 具備不一樣的完成時間、不一樣的完成速率的系統,好比在線購物系統,不一樣的人完成一次在線購物,所購買的產品、所耗費的時間可能有很大差別;
NONE:表示不啓用 LBA。
若是再結合 service 的 CLB_GOAL 和 GOAL,以及 v$servicemetric,就能夠概括出 Oracle RAC 10gR2 裏 Server 端 Connect Time Load Balancing 判斷負載的依據:
一、Oracle RAC 10gR2 裏 Server 端 Connect Time Load Balancing 默認狀況下判斷負載的依據是鏈接到每一個節點的 session 的數量,即當 CLB_GOAL 爲默認值 LONG 的時候,v$servicemetric 的對應 service 的 GOODNESS=number of connected sessions,DELTA=1,注意此時 LBA 並無啓用;
二、Oracle RAC 10gR2 裏若是把 service 的 CLB_GOAL 設爲 SHORT,同時把 GOAL 設爲 THROUGHPUT 或 SERVICE_TIME,則意味着 Server 端 Connect Time Load Balancing 判斷節點負載的依據是 LBA。此時若是 GOAL 設爲 THROUGHPUT,則 v$servicemetric 的對應 service 的 GOODNESS 值是根據 CPUPERCALL 和 DBTIMEPERCALL 來計算;若是 GOAL 設爲 SERVICE_TIME,則 v$servicemetric 的對應 service 的 GOODNESS 值是根據 CALLSPERSEC 和 DBTIMEPERSEC 來計算。
接下來再看一下 Oracle RAC 11gR2 下的 Server 端 Connect Time Load Balancing:
Oracle RAC 11gR2 下的 Server 端 Connect Time Load Balancing 和 Oracle RAC 10gR2 下的 Server 端 Connect Time Load Balancing 相似,只不過由於 Oracle RAC 11gR2 裏引入了 SCAN,因此 Oracle RAC 11gR2 環境下 remote_listener 應設置爲 SCAN:port。
這裏假設是一個 4 節點的 Oracle RAC 11gR2 環境,tnsnames.ora 中的鏈接串是以下這樣:
(DESCRIPTION = (FAILOVER=ON)(LOAD_BALANCE=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = MySCAN)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME =RAC11g)))
再在各個節點的 tnsnames.ora 中加入以下設置(注意這裏監聽的是各個節點的 VIP):
LISTENER_RAC1 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC1-vip)(PORT = 1521)) LISTENER_RAC2 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC2-vip)(PORT = 1521)) LISTENER_RAC3 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC3-vip)(PORT = 1521)) LISTENER_RAC4 = (ADDRESS = (PROTOCOL = TCP)(HOST = RAC4-vip)(PORT = 1521))
而後只須要在初始化參數文件里加入以下設置就能實現 Server 端 Connect Time Load Balancing 了:
RAC1.local_listener=LISTENER_RAC1 RAC2.local_listener=LISTENER_RAC2 RAC3.local_listener=LISTENER_RAC3 RAC4.local_listener=LISTENER_RAC4 *.remote_listener= MySCAN:1521
當作了上述設置後,Oracle 11gR2 RAC 的 Server 端 Connect Time Load Balancing 也就配好了。此時全部的 SCAN Listener 其實是都知道全部RAC節點的負載狀況的。當先通過一層客戶端的 Connect Time Load Balancing,好比這裏隨機的連到了第二個 SCAN VIP 所對應的 SCAN Listener 上後,這時候這個 SCAN Listener 會選擇一個實際負載較低的 RAC 節點,而後把鏈接請求轉移(redirect)到這個負載較低的 RAC 節點的 Local Listener 上——這就是 Oracle RAC 11gR2 的 server 端的 Connect Time Load Balancing。
實際上,local_listener 和 remote_listener 支持複雜的鏈接串的寫法。因此,能夠在初始化參數裏面直接設置 local_listener 和 remote_listener,而不須要在 $ORACLE_HOME/network/admin 下的 tnsnames.ora 中作上述設置。
來看一個兩節點的 Oracle 11gR2 RAC 的實例。這個環境中用了 hosts 文件,hosts 文件內容以下所示:
10.1.15.64 P550-05-LA 10.1.15.84 P550-05-LA-vip 9.2.1.64 P550-05-LA-priv 10.1.15.65 P550-05-LB 10.1.15.85 P550-05-LB-vip 9.2.1.65 P550-05-LB-priv 10.1.15.86 nbsdev-scan
從上述內容能夠看到,如今節點 1 的 vip 是 10.1.15.84,節點 2 的 vip 是 10.1.15.85,整個 RAC 環境的 SCAN vip 是 10.1.15.86。
先登錄節點 1,看一下節點 1 上的 local_listener 和 remote_listener 的設置: SQL> show parameter instance_name;
NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ instance_name string NBSDEV1
SQL> show parameter local_listener;
NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ local_listener string (DESCRIPTION=(ADDRESS_LIST= (ADDRESS=(PROTOCOL=TCP)(HOST=10.1.15.84)(PORT=1522))))
SQL> show parameter remote_listener;
NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ remote_listener string nbsdev-scan:1522
再登錄節點 2,看一下節點 2 上的 local_listener 和 remote_listener 的設置: SQL> show parameter instance_name;
NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ instance_name string NBSDEV2
SQL> show parameter local_listener;
NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ local_listener string (DESCRIPTION=(ADDRESS_LIST=(ADDRESS= (PROTOCOL=TCP)(HOST=10.1.15.85)(PORT=1522))))
SQL> show parameter remote_listener;
NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ remote_listener string nbsdev-scan:1522
上述環境 server 端的 Connect Time Load Balancing 實際上已經配置好了,但從以下內容能夠看到,咱們並無在 $ORACLE_HOME/network/admin 下的 tnsnames.ora 中配置相關的 local_listener 和 remote_listener:
ora11g:/nbsdu01/app/oracle/product/11.2/network/admin>cat tnsnames.ora
# tnsnames.ora Network Configuration File: /nbsdu01/app/oracle/product/11.2/network/ admin/tnsnames.ora # Generated by Oracle configuration tools. NBSDEV = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = nbsdev-scan)(PORT = 1522)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = NBSDEV) ) )
Runtime Connection Load Balancing 是指從鏈接池中取得已有鏈接時的 Connection Load Balancing。
以前不管是 Oracle RAC 9iR2/10gR2,仍是 Oracle RAC 11gR2,在存在鏈接池的狀況下,單純的 server 端的 Connect Time Load Balancing 並不能保證當應用須要從鏈接池裏取得一個已有鏈接的時候,這個鏈接就指向了節點負載較低的那個節點。由於這個時候應用從鏈接池裏取得的鏈接極可能就是鏈接池初始化的時候造成的鏈接,只是反映了鏈接池初始化那個時間點的各個節點的負載狀況,而隨着時間的推移,各個節點的負載狀況可能發生了很大的變化,因此這種狀況下鏈接池的鏈接極可能並非真正的 Load Balance。
實際上 FAN 就是爲了解決上述問題而設計的。能支持 FAN events 的鏈接池經過訂閱 FAN HA events,就能夠保證當應用須要從鏈接池裏取得一個已有鏈接的時候,這個鏈接確定是有效的鏈接,不會指向那些 service 宕掉或者 instance 崩潰的節點(以前已經提到過,這是經過 FCF 來實現的:當支持 FAN events 的鏈接池接收到包含 instance/service 宕掉的 FAN HA events 後,原先 cache 在鏈接池裏的跟這個 instance/service 相關的鏈接立刻會被標記爲失效,同時這些鏈接會被清除);另一個方面,能支持 FAN events 的鏈接池經過訂閱 LBA events,就能近乎實時地知道各個 RAC 節點實際的負載狀況,因此當應用須要從鏈接池裏取得一個已有鏈接的時候,鏈接池就能提供給用戶一個真正的負載較低的 RAC 節點,這樣就實現了真正的 Runtime Connection Load Balancing。
如今介紹兩個經過訂閱 LBA events 實現 Runtime Connection Load Balancing 的例子。
例三:JDBC Runtime Connection Load Balancing
這裏的 JDBC 鏈接是指 JDBC thin 鏈接,要實現 JDBC Runtime Connection Load Balancing,只須要作以下兩步便可:
一、首先要按照「例一:JDBC Fast Connection Failover (FCF)」裏那樣把 JDBC FCF 設置好;
二、啓用 LBA events:
srvctl modify service -d RAC11g -s Email -B SERVICE_TIME -j SHORT
這裏首先把 CLB_GOAL 設置成了 SHORT,接着把 GOAL 設置成了 SERVICE_TIME,這二者缺一不可,CLB_GOAL 和 GOAL 的各個值的詳細含義已經在 Connect Time Load Balancing 裏詳細解釋過,這裏再也不贅述。
例四:ODP.NET Runtime Connection Load Balancing
ODP.NET的Runtime Connection Load Balancing的 啓用跟「例二:ODP.NET Fast Connection Failover (FCF)」裏的步驟相似,只須要作以下 4 步就好,注意這裏第 1 步和第 4 步跟「例二:ODP.NET Fast Connection Failover (FCF)」裏的相應步驟是不同的:
一、把對應 service的AQ Notification 打開,同時設置 CLB_GOAL和GOAL:
srvctl modify service -d RAC11g -s Email -q TRUE -B SERVICE_TIME -j SHORT
二、把 aq_tm_processes 的值設爲 1;
三、賦予指定用戶 de-queue 的權限:
exec dbms_aqadm.grant_queue_privilege('DEQUEUE','SYS.SYS$SERVICE_METRICS', <your username=>);
四、在.NET鏈接串裏設置 Load Balancing=true,以下所示:
con.ConnectionString = "User Id=user_name;Password=password;Data Source=odpapp;" + "Min Pool Size=10;Connection Lifetime=120;Connection Timeout=60;" + "Load Balancing=true;Incr Pool Size=5;Decr Pool Size=2";
至此咱們已經詳細的描述了 RAC 環境下的鏈接管理。
做爲這篇文章的結束,最後咱們來闡述一下如何在 Oracle 數據庫裏設置 TCP timeout。
一、32 位 Windows上Oracle 數據庫 11.2.0.1 默認的操做系統TNS鏈接 timeout 的時間大概是 20 秒:
16:27:26 SQL> conn scott/tiger@cuihua112;
ERROR: ORA-12170: TNS: 鏈接超時 16:27:49 SQL>
這裏能夠看到,從開始鏈接到鏈接超時的間隔時間是 23 秒,去掉輸入上述鏈接串「conn scott/tiger@cuihua112」所耗費的時間,能夠知道在 32 位 Windows上Oracle 數據庫 11.2.0.1 默認的操做系統TNS鏈接 timeout 的時間大概是 20 秒。
二、修改一下 client 端 sqlnet.ora 文件,將 TNS 鏈接 timeout 時間修改成 5 秒(這是經過設置 SQLNET.OUTBOUND_CONNECT_TIMEOUT 來實現的):
# sqlnet.ora Network Configuration File: C:\app\cuihua\product\11.2.0\dbhome_1\network\admin\sqlnet.ora # Generated by Oracle configuration tools. # This file is actually generated by netca. But if customers choose to # install "Software Only", this file wont exist and without the native # authentication, they will not be able to connect to the database on NT. SQLNET.AUTHENTICATION_SERVICES= (NTS) SQLNET.OUTBOUND_CONNECT_TIMEOUT = 5 NAMES.DIRECTORY_PATH= (TNSNAMES, EZCONNECT)
從以下結果能夠看到,從開始鏈接到鏈接超時的間隔時間是 7 秒,去掉輸入上述鏈接串「conn scott/tiger@cuihua112」所耗費的時間,能夠知道上述 5 秒超時的設置確實生效了。
16:28:34 SQL> conn scott/tiger@cuihua112; ERROR: ORA-12170: TNS: 鏈接超時 16:28:41 SQL>
三、註釋掉上述 SQLNET.OUTBOUND_CONNECT_TIMEOUT = 5,在 tnsnames.ora 的 cuihua112 的鏈接串中將 TNS 鏈接 timeout 時間設置爲 15 秒:
CUIHUA112 = (DESCRIPTION = (CONNECT_TIMEOUT=5)(RETRY_COUNT=2) (ADDRESS = (PROTOCOL = TCP)(HOST = 172.20.190.11)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = cuihua112) ) )
從以下結果能夠看到,從開始鏈接到鏈接超時的間隔時間是 17 秒,去掉輸入上述鏈接串「conn scott/tiger@cuihua112」所耗費的時間,能夠知道上述 15 秒超時的設置確實生效了。
16:31:08 SQL> conn scott/tiger@cuihua112; ERROR: ORA-12170: TNS: 鏈接超時 16:31:25 SQL>
四、同時啓用 SQLNET.OUTBOUND_CONNECT_TIMEOUT = 5 和上述 cuihua112 的鏈接串,從結果裏能夠看到,tnsnames.ora 中的設置取代了(override)了 sqlnet.ora 中的 TNS 鏈接 timeout 的設置。即在同時啓用的狀況下,如今的 TNS 鏈接 timeout 設置仍是爲 15 秒。
16:33:12 SQL> conn scott/tiger@cuihua112; ERROR: ORA-12170: TNS: 鏈接超時 16:33:29 SQL>
這裏能夠看到,從開始鏈接到鏈接超時的間隔時間是 17 秒,去掉輸入上述鏈接串「conn scott/tiger@cuihua112」所耗費的時間,能夠知道是 15 秒超時的設置生效了。
五、在 tnsnames.ora 的 cuihua112 的鏈接串中將 TNS 鏈接 timeout 時間設置爲 40 秒,這已經超過了 TNS 鏈接默認的 timeout 值,從以下測試裏能夠看到,Oracle 數據庫會以 tnsnames.ora 中的設置爲準(固然,這裏前提條件是單次鏈接的 CONNECT_TIMEOUT 設置不要超過操做系統 TNS 鏈接默認的 timeout 值,若是超過了則 CONNECT_TIMEOUT 設置失效,但 RETRY_COUNT 的設置依然有效)。
CUIHUA112 = (DESCRIPTION = (CONNECT_TIMEOUT=10)(RETRY_COUNT=3) (ADDRESS = (PROTOCOL = TCP)(HOST = 172.20.190.11)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = cuihua112) ) )
這裏 CONNECT_TIMEOUT 設置爲 10 秒,RETRY_COUNT 設置爲 3,實際上就將 TNS 鏈接 timeout 時間設置成了40秒
16:52:52 SQL> conn scott/tiger@cuihua112; ERROR: ORA-12170: TNS: 鏈接超時 16:53:33 SQL>
這裏能夠看到,從開始鏈接到鏈接超時的間隔時間是 41 秒,去掉輸入上述鏈接串「conn scott/tiger@cuihua112」所耗費的時間,能夠知道是 40 秒超時的設置生效了。
這篇文章詳細介紹了 RAC 環境下的鏈接管理及其相關內容,主要針對 RAC 環境下鏈接管理所涉及到的 Connect Time Load Balancing、Runtime Connection Load Balancing、Connect Time Connection Failover 和 Runtime Connection Failover 等內容,同時也描述了包括 TAF、ONS、FCF、FAN 和 LBA 等其它一些相關內容。