Oracle RAC 環境下的連線管理

531968912發表於2018-06-20

Oracle RAC 環境下的連線管理


作者:崔華

這篇文章詳細介紹了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,以體現他們之間在連線管理上的差異。

所謂“連線管理”,主要體現在 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


Connect Time Connection Failover 是指不從連線池中取得已有連線,而是直接連線 Oracle 資料庫時的 Failover。在 Oracle RAC 11gR2 之前,Connect Time Connection Failover 是非常容易實現的,只需要在相關的 tnsnames.ora 中指定多個 vip,同時指定 FAILOVER=ON 就好了。如下所示:

(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 的架構圖:

Oracle RAC 環境下的連線管理

 

如上圖所示,如果使用了 DNS 或者 GNS (Grid Naming Service),那麼最多可以有 3 個 SCAN VIP 和 3 個 SCAN Listener;如果沒有使用 DNS 或者 GNS,而是選擇使用 hosts 檔案,則只會有 1 個 SCAN VIP 和 1 個 SCAN Listener。

這裡假設在 tnsnames.ora 中這樣配置:

  (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。

當使用了 hosts 檔案來指定 SCAN VIP 的時候,即在整個 RAC 環境只有 1 個 SCAN VIP 的情況下,Failover 其實也存在,只不過這種情況下 Failover 的速度會慢一些。因為當 SCAN VIP 所在的節點宕掉後,SCAN VIP 會和相關的 SCAN Listener 一起整體 Failover 到其他節點,只不過這個 Failover 需要時間,而客戶端需要等待這個 Failover 過程完畢後才能重新連上 RAC。

二、接下來介紹 Runtime Connection Failover


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 有如下一些知識點需要我們注意:

1、它可以在 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  -s  [-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 ] [-w ]
    -d       Unique name for the database
    -s              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 of the service (primary, physical_standby, 
logical_standby, snapshot_standby)
    -y               Management policy for the service (AUTOMATIC or MANUAL)
    -e        Failover type (NONE, SESSION, or SELECT)
    -m      Failover method (NONE or BASIC)
    -w              Failover delay
    -z              Failover retries
    -j   Connection Load Balancing Goal (SHORT or LONG). Default is LONG.
    -B      Runtime Load Balancing Goal (SERVICE_TIME, 
THROUGHPUT, or NONE)
    -x   Distributed Transaction Processing (TRUE or FALSE)
    -q  AQ HA notifications 
(TRUE or FALSE)
    -h                       Print usage
	

2、當 TAF 的TYPE 設定為 select 的時候,單純 select 操作(不包括 select … for update)可以做到“斷點續傳”,即單純的 select 操作在利用 TAF 實現 Failover 後是可以從中斷的地方繼續往下執行的;
3、TAF 對 DML 操作不能做到“斷點續傳”,即如果一個 transaction 在使用 TAF 實現 Failover 後,該 transaction 不能從中斷的地方繼續執行,需要再次從頭開始執行;
4、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 的傳播途徑:

 

Oracle RAC 環境下的連線管理

 

訂閱 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:
1、把 implicit connection cache 開啟;
2、把 FastConnectionFailoverEnabled 開啟;
3、最好是直接訂閱遠端的 ONS(在Oracle 10gR2 之前的版本不能直接訂閱遠端的 ONS,只能通過在本地安裝 ONS 後來實現 FAN events 的中轉);
4、最好是在 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 了:
1、把對應 service 的 AQ Notification 開啟:
srvctl modify service -d RAC11g -s Email -q TRUE
2、把 aq_tm_processes 的值設為 1;
3、賦予指定使用者 de-queue 的許可權:
exec dbms_aqadm.grant_queue_privilege('DEQUEUE','SYS.SYS$SERVICE_METRICS', );
4、在 .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


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_來控制 Oracle RAC 9iR2 資料庫到底採用哪種判斷依據。prefer_least_loaded_node_ 的預設值是 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 判斷負載的依據:

1、Oracle RAC 10gR2 裡 Server 端 Connect Time Load Balancing 預設情況下判斷負載的依據是連線到每個節點的 session 的數量,即當 CLB_GOAL 為預設值 LONG 的時候,v$servicemetric 的對應 service 的 GOODNESS=number of connected sessions,DELTA=1,注意此時 LBA 並沒有啟用;
2、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


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,只需要做如下兩步即可:
1、首先要按照“例一:JDBC Fast Connection Failover (FCF)”裡那樣把 JDBC FCF 設定好;
2、啟用 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)”裡的相應步驟是不一樣的:
1、把對應 service的AQ Notification 開啟,同時設定 CLB_GOAL和GOAL:
srvctl modify service -d RAC11g -s Email -q TRUE -B SERVICE_TIME -j SHORT
2、把 aq_tm_processes 的值設為 1;
3、賦予指定使用者 de-queue 的許可權:
exec dbms_aqadm.grant_queue_privilege('DEQUEUE','SYS.SYS$SERVICE_METRICS', );
4、在.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。

1、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 秒。

2、修改一下 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>

3、註釋掉上述 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>

4、同時啟用 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 秒超時的設定生效了。

 

5、在 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 等其它一些相關內容。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26442936/viewspace-1064199/,如需轉載,請註明出處,否則將追究法律責任。

相關文章