暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

关于RAC数据库load balance案例分析

原创 熊军 2019-07-24
2602

在这一节我将通过两个案例入手,来讲一下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上然后重定向过来的。


最后修改时间:2019-07-24 17:09:13
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论