文章转自: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 等其它一些相关内容。