在这一节我将通过两个案例入手,来讲一下RAC数据库load balance相关的问题。两个案例会以比较直接简单的方式进行描述,对于案例中涉及的深入的知识将在后面展开。
案例一:一套2节点的Oracle 10g RAC数据库。系统维护人员报告称不能实现负载均衡,大部分的会话都连接到了第二个节点上。 环境是Oracle 10.2.0.3 for AIX。
分析过程如下所示。
(1)连接到数据库,检查数据库连接,发现实例resrac2的连接数超过2000,而实例resrac1的连接数不到300。相差非常大。
(2)检查2个节点的监听状态,没有异常。
(3)查看应用的的TNS配置,没有发现异常。
(4)仔细检查2个节点的连接,发现几乎所有的JAVA应用都连接到了resrac2,而其他的非JAVA应用,大部分也同样是连接到了resrac2。
(5)使用SQL*Plus进行连接测试,发现所有的连接都被重定向了resrac2。但是修改TNSNAME,指定INSTANCE_NAME为resrac1时,能够正常连接resrac1,这表明实例resrac1本身连接接受连接。
(6)检查listener.log日志,发现PMON能够正常进行监听注册和服务更新(Service Update)。
(7)检查发现ONS没有启动,手工启动ONS后,问题依然存在。
(8)重启监听,问题依然存在。
(9)检查服务状态,具体如下所示。
SQL> select name,goal,clb_goal from dba_services; NAME GOAL CLB_G ---------------------------------------------------------------- ------------ ----- SYS$BACKGROUND NONE SHORT SYS$USERS NONE SHORT seeddataXDB LONG seeddata LONG resracXDB LONG resrac NONE LONG
可以看到resrac服务配置正常。
(10)检查Service Metric数据,具体如下所示。
SQL> select inst_id,service_name,goodness,delta from gv$servicemetric where service_name='resrac'; INST_ID SERVICE_NAME GOODNESS DELTA ---------- --------------- ---------- ---------- 1 resrac 6999 1 1 resrac 6999 1 2 resrac 1976 1 2 resrac 1976 1
从上面的数据可以看出,第1个实例resrac1存在问题。实例resrac2的goodness值与以resrac服务连接的连接数是接近相等的(这跟更新的周期有关)。但实例resrac1的goodness值远远大于resrac1的实际连接数。
Goodness值有如下两个特点。
Goodness值在Service的CLB_GOAL为CLB_GOAL_LONG时表示实例中该服务的会话连接数。此值越大,连接越多,那么连接时就不会连接到goodness值大的实例上,而是连接到值小的实例上。
Goodness值由MMON进程进行计算,然后定期由PMON发布给监听。
检查MMON进程没有发现异常。
$ truss -p 1467404 thread_wait(3000) (sleeping...) thread_wait(3000) = 1 thread_wait(3000) Err#4 EINTR Received signal #14, SIGALRM [caught] sigprocmask(0, 0x0FFFFFFFFFFFC1E0, 0x0000000000000000) = 0 _poll(0x0FFFFFFFFFFFB250, 1, 0) = 0 sigprocmask(0, 0x0FFFFFFFFFFFBEB8, 0x0000000000000000) = 0 incinterval(0, 0x0FFFFFFFFFFFBC50, 0x0FFFFFFFFFFFBC70) = 0 sigprocmask(1, 0x0FFFFFFFFFFFBDF0, 0x0000000000000000) = 0 incinterval(0, 0x0FFFFFFFFFFFC060, 0x0FFFFFFFFFFFC080) = 0 sigprocmask(1, 0x0FFFFFFFFFFFC1E0, 0x0000000000000000) = 0 ksetcontext_sigreturn(0x0FFFFFFFFFFFC320, 0x000000011023F1C8, 0x0000000000000000, 0x800000000000D0B2, 0x0000000000000000, 0x0000000000000134, 0x0000000000000000, 0x0000000000000000) thread_wait(1230) = 1 times(0x0FFFFFFFFFFFC8B0) = 1776218558 times(0x0FFFFFFFFFFFC800) = 1776218558 thread_wait(3000) (sleeping...) thread_wait(3000) = 1 thread_wait(3000) Err#4 EINTR Received signal #14, SIGALRM [caught] sigprocmask(0, 0x0FFFFFFFFFFFC1E0, 0x0000000000000000) = 0
(11)从上面的分析可以看到,Service Metric的数据异常,导致了实例resrac1不能接受新的连接。由于resrac1实例的resrac服务的goodness值异常得高,怀疑是Bug引起。
(12)搜索Oracle的Bug数据库,发现与下面的Bug匹配。
Bug 6442431 A node may stop receiving connections with RAC load balancing Details: In a RAC cluster using server side load balancing, one of the nodes may stop receiving any new connections even though it is idle. Fixed-Releases: 10.2.0.4 & 11.1.0.7 & 11.2
但是当前的版本没有此Bug的补丁,只有升级到10.2.0.4才能解决。
(13)解决办法有如下3种。
1)重启实例resrac1,但是以后Bug可能被再次触发。
2)将Oracle数据库升级到10.2.0.4。
3)关闭服务端(Server Side)的Load balancing,修改应用的连接配置,使用客户端连接时负载均衡(Connection Time Load Balancing)。
案例二:一套2节点的Oracle 11g RAC Active Data Guard,应用通过SCAN 地址连接到数据库,绝大部分连接都连到了节点1上。
环境:某银行重要交易系统,Oracle 11.2.0.4 for Linux,2节点RAC主库+2节点本地RAC ADG+2节点同城异机房RAC ADG,读写分离架构,ADG用于只读查询,主库实现事务交易,分布式存储架构,同城异机房实现容灾。SCAN地址通过域名解析。
问题分析和处理思路与前面的案例大体一致,关键的差异在于如下一些要点。
(1)Active Data Guard指定与主库不同的db_unique_name,那么默认注册到监听的服务与db_unique_name是一致的。但是这个服务名通过dba_services视图查询并没有。 INST_ID TYPE SERVICE_NAME COUNT(*) ---------- ---------- ------------------------------ ---------- 1 USER cusr 226 1 USER SYS$USERS 4 2 USER cusr 218 1 BACKGROUND SYS$BACKGROUND 50 2 USER SYS$USERS 2 2 BACKGROUND SYS$BACKGROUND 50
(2)查询v$servicemetric视图也没有这个服务的统计数据。
INST_ID SERVICE_NAME GOODNESS DELTA ---------- ------------------------------ ---------- ---------- 1 cusrXDB 0 1 1 cusrXDB 0 1 1 SYS$BACKGROUND 2150 50 1 SYS$BACKGROUND 2150 50 1 SYS$USERS 4100 50 1 SYS$USERS 4100 50 2 cusrXDB 0 1 2 cusrXDB 0 1 2 SYS$BACKGROUND 2100 50 2 SYS$BACKGROUND 2100 50 2 SYS$USERS 0 100 2 SYS$USERS 0 100
那么,会不会是因为pmon不能发布服务的负载信息呢?好在有这样一个事件10257。
SQL> oradeBug setospid <pmon的pid> SQL> oradeBug event 10257 trace name context forever,level 16
在pmon的trace文件中不断重复出现如下信息。
err=-300 lbflgs=0x0 tbtime=0 tntime=0 etime=300 srvs=1 nreqs=0 sreqs=0 asrvs=1 error=-300 etime=300 control=0 integral=0 lasterr=-300 lastetm=300 kmmlrl: status: succ=4, wait=0, fail=0
我们需要有点耐心,一会儿会出现下示情况。
err=-300 lbflgs=0x0 tbtime=0 tntime=0 etime=300 srvs=1 nreqs=0 sreqs=0 asrvs=1 error=-300 etime=300 control=0 integral=0 lasterr=-301 lastetm=301 kmmlrl: status: succ=4, wait=0, fail=0 kmmlrl: update for time delta: 60019 kmmgdnu: cusrXDB goodness=0, delta=1, flags=0x5:unblocked/not overloaded, update=0x6:G/D/- kmmlrl: node load 284 kmmlrl: D000 load 0 kmmlrl: nsgr update returned 0
如果我们手工执行alter system register命令,会产生如下日志。
kmmlrl: status: succ=4, wait=0, fail=0 kmmlrl: register now kmmgdnu: cusrXDB goodness=0, delta=1, flags=0x5:unblocked/not overloaded, update=0x6:G/D/- kmmlrl: 556 processes kmmlrl: node load 266 kmmlrl: instance load 508 kmmlrl: nsgr update returned 0 kmmlrl: nsgr register returned 0
应用连的服务名是cusrdg,很显然这个并不在里面。以上输出信息中,kmmlrl是Kernel Message Monitor Listener Register Load的含义,kmmgdnu 是 KMM GooDNess Update的含义。
那么,到现在要的方向就是pmon没有发布cusrdg这个服务,如何来验证这个问题?好在有一套测试环境,是同样的架构。在测试环境中,使用ADG的db_unique_name指定的服务,同样不能达到负载均衡。但是如果用dbms_service.add_service增加一个服务(需要在主库上操作),然后在备库启用这个服务,使用这个服务则可以正常进行负载均衡。
当然也可以用srvctl add service来增加服务,但使用srvctl start service时,同样会去调用dbms_service的add_service命令。
使用的测试脚本具体如下所示。
#!/bin/sh SQLplus system/xxxx/@SCAN域名/service_name << EOF exec dbms_lock.sleep(120); EOF
将上述脚本在后台跑上几十个,检查gv$session就可以发现负载均衡是否起作用。我们在测试ADG库上看到的10257 event trace的输出如下所示。
kmmlrl: status: succ=4, wait=0, fail=0 kmmlrl: status: succ=4, wait=0, fail=0 kmmlrl: register now kmmgdnu: drtestsvc goodness=0, delta=1, flags=0x4:unblocked/not overloaded, update=0x6:G/D/- kmmgdnu: oradbXDB goodness=0, delta=1, flags=0x5:unblocked/not overloaded, update=0x6:G/D/- kmmlrl: node load 10 kmmlrl: nsgr update returned 0 kmmlrl: nsgr register returned 0
可以看到有drtestsvc这个单独建的服务名,这个服务名出现在pmon向监听注册的信息中。
要继续解析这个问题,还必须了解RAC数据库负载均衡原理。负载均衡分两种,Client Side Load Balance和Server Side Load Balance。
客户端的负载均衡通过连接描述符来指定,具体如下所示。
(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.22.24)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.22.44)(PORT = 1521)) ) (LOAD_BALANCE = yes) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = resrac) )
LOAD_BALANCE=yes表示连接时会随机从ADRESS LIST中选择一个地址进行连接。要实现LOAD BALANCE,那么不能指定INSTANCE_NAME,也不能指定SID,同时连接时“面向服务”。客户端连接描述符在指定多个地址还默认打开了failover,也就是一个地址连接不上就会连接另一个地址。
服务器端的负载均衡,又是怎么一种机制呢? 10g和11g有很大的不同,这里以11g RAC为例来解释,如图12-1所示。
图12-1
我们配合监听的信息来展示一下(这是在第2个节点上获得的信息)。
lsnrctl services LISTENER_SCAN1 Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=LISTENER_SCAN1))) Services Summary... Service "appadb" has 2 instance(s). Instance "appadb1", status READY, has 2 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.233.143)(PORT=1521))) "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.233.143)(PORT=1522))) Instance "appadb2", status READY, has 2 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.233.145)(PORT=1521))) "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.233.145)(PORT=1522)))
VIP监听信息如下所示。
lsnrctl services LISTENER LSNRCTL for Linux: Version 11.2.0.3.0 - Production on 17-DEC-2015 17:16:35 Copyright (c) 1991, 2011, Oracle. All rights reserved. Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=LISTENER))) Services Summary... Service "+ASM" has 1 instance(s). Instance "+ASM2", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready LOCAL SERVER Service "appadb" has 1 instance(s). Instance "appadb2", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:1858112 refused:0 state:ready LOCAL SERVER The command completed successfully
打开数据库后,pmon进程会向监听注册服务。
SQL> show parameter listener NAME TYPE VALUE ------------------------------------ ----------- ------------------------------ listener_networks string local_listener string (DESCRIPTION=(ADDRESS_LIST=(AD DRESS=(PROTOCOL=TCP)(HOST=133. 37.233.145)(PORT=1522))(ADDRES S=(PROTOCOL=TCP)(HOST=192.168.2 33.145)(PORT=1521)))) remote_listener string appadb-scan:1521
remote_listener指向SCAN LISTENER,local_listener指向本节点VIP监听。这里有一个PORT为1522的是一个单独的监听,这里可以先忽略。
我们会看到,VIP监听上的服务只有一个实例,即本节点的实例的服务。
Service "appadb" has 1 instance(s). Instance "appadb2", status READY, has 1 handler(s) for this service... Handler(s): "DEDICATED" established:1858112 refused:0 state:ready LOCAL SERVER
但是SCAN监听上有两个实例。
Service "appadb" has 2 instance(s). Instance "appadb1", status READY, has 2 handler(s) for this service... …. Instance "appadb2", status READY, has 2 handler(s) for this service... Handler(s): "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.169.233.145)(PORT=1521))) "DEDICATED" established:0 refused:0 state:ready REMOTE SERVER (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.169.233.145)(PORT=1522)))
REMOTE SERVER表示,这个实例是需要通过另一个监听来跳转的。这个监听不能直接连接到该实例上。对于SCAN监听,所有的实例都是远程实例。
那么REMOTE SERVER中的信息是从哪里来,实际上是PMON进程将local listener的信息告诉SCAN监听。大家可以看到,local listener设定的信息跟SCAN LISTENER中Remote Server的信息是一样的。
了解了这些之后,下面来看一下如何实例负载均衡,客户端与数据库的交互顺序如下所示。
(1)客户端连接到SCAN监听。
(2)SCAN监听根据客户端连接时指定的条件(SERVICE_NAME),选择一个实例。
(3)SCAN监听把选定的实例的信息,包括地址,端口,协议,实例名发给客户端。
(4)客户端根据这个信息,重新发起一次网络连接,这次连接到实例的VIP监听上,VIP监听的服务是LOCAL SERVER,所以它会建立真正的连接。
(5)接下来就是登录验证。
这个过程有2个关键点。
(1)上述第2步的实例选择中,监听是根据从PMON得到的实例的负载信息来选择实例的。这一步实现了负载均衡。如果连接SCAN时,指定了INSTANCE NAME参数,那SCAN监听只会重定向到指定参数。
(2)如果local listener参数设置不对(正常情况下,11g不需要显式设置,由Grid Infrastructure设置好就行),那么将不能连接,会报错。这个参数正常指向VIP地址,如果设置为主机名或域名,但是客户端不能解析,就连接不上。
到这里,会有一个跟网络安全相关的话题。有的企业的应用是在另外的网络,中间有网闸或NAT,连接SCAN监听时,用的一个外网地址,外网地址通过NAT转换,得到内网的地址。例如,外网是10.10.100.144:1521,内网是192.168.100.144:1521,有一个NAT设备进行转换。SCAN监听会返回一个VIP监听地址,不幸的是,它会返回内网local listener地址——192.168.100.141:1521,客户端在外网连接这个地址肯定是不通的。那是否可以将数据库的参数设置为外网地址呢?这样的话它就注册不到vip 监听上了。这个解决方案也有问题。
这样的方式下,用客户端的负载均衡,直接连接VIP地址是更好的选择。另外,一些比较老的JDBC驱动,不能识别SCAN监听发回的要求重定向的协议,这种驱动连接数据库也有问题。
在监听的信息中,会有如下这样的信息。
Handler(s): "DEDICATED" established:0 refused:0 state:ready
这条信息表示接受这个服务的处理器(实例)的状态ready,已经拒绝多少个连接,建立了多少连接。显然这个应用没有连接SCAN 监听,因为这个监听的所有服务的连接数都是0.
只有本地的VIP监听才显示有连接。
Handler(s): "DEDICATED" established:1858112 refused:0 state:ready LOCAL SERVER
另外,在VIP监听日志中会出现如下所示的信息。
10-DEC-2015 22:29:01 * (CONNECT_DATA=(CID=(PROGRAM=JDBC Thin Client)(HOST=__jdbc__) (USER=app))(SERVER=DEDICATED)(SERVICE_NAME=cusrdg)(FAILOVER_MODE=(TYPE=session)(METHOD=basic))) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.13.33)(PORT=44885)) * establish * cusrdg * 0
这表示直接连接或本机SCAN LISTENER连接的。如果有INSTANCE_NAME=这样的字样,则表示是另一个节点转发过来的。我们去检查监听日志,可以查看这个有用的信息。
grep “service update” listener.log grep INSTANCE_NAME= listener.log
如果有这样的数据,表明客户端连接指定了INSTANCE_NAME,这个可能是连接串指定了(当前这种很少)。对于负载均衡来说,更多的是客户端开始连接到另外节点的SCAN LISTENER上然后重定向过来的。