Tips for Installing Oracle11gR2 RAC on AIX 6.1

不准备写一篇完整的Installation Guide,安装光盘中自带的pdf文档已经足够。本文只是总结一些在安装过程中碰到的问题或者说应该要注意的要点。

1. 如下命令的软件包需要配全,通常在安装完操作系统以后就应该已经都有了。

lslpp -l bos.adt.base bos.adt.lib bos.adt.libm bos.perf.libperfstat \
 bos.perf.perfstat bos.perf.proctools rsct.basic.rte rsct.compat.clients.rte xlC.aix61.rte

2. 安装文档中提到的fix即使不存在也不影响安装。

# instfix -i -k "IZ41855 IZ51456 IZ52319"
    There was no data for IZ41855 in the fix database.
    All filesets for IZ51456 were found.
    There was no data for IZ52319 in the fix database.

3. 11gR2 RAC自带CTSS时间同步服务,因此安装文档中要求禁用NTP,但是在安装过程中最后检查的时候,仍然会报NTP服务无法使用,可以直接忽略。

4. 11gR2 RAC安装中对于用户和用户组的建议可以说比以前复杂很多,不再仅仅是oinstall和dba这两个用户组。为了方便我们仍然可以只创建oinstall和dba这两个用户组,但是建议按照安装文档中描述的那样创建grid和oracle这两个用户,用grid用户安装Grid Infrastructure,用oracle用户安装RAC。

5. 11gR2中OCR和Voting是可以放置在ASM磁盘组中,因此实际上在整个数据库环境中,应该会存在至少三个ASM Disk Group,也就是crsdg(用于GRID使用)、datadg(数据库数据文件)、fradg(闪回区)。这里需要特别注意,所有磁盘组都是用grid用户执行asmca来创建的(当然你可以用create diskgroup命令创建),而最后数据库实例是以oracle用户启动的,也就是oracle用户也必须有读写磁盘组中disk的权限。

假设我们的环境中有rhdisk2、rhdisk3、rhdisk4三个LUN分别对应crsdg、datadg和fradg,那么建议做如下的权限设置:

chown grid:oinstall /dev/rhdisk2
chown grid:oinstall /dev/rhdisk3
chown grid:oinstall /dev/rhdisk4
chmod 660 /dev/rhdisk3
chmod 660 /dev/rhdisk4

# ls -l /dev/rhdisk*
crw-------    1 grid     oinstall     23,  3 Jun 01 16:23 /dev/rhdisk2
crw-rw----    1 grid     oinstall     23,  4 Jun 01 16:13 /dev/rhdisk3
crw-rw----    1 grid     oinstall     23,  2 Jun 01 16:13 /dev/rhdisk4

6. 新增的SCAN VIP其实可有可无,特别对于不会频繁增删数据库节点的环境,个人觉得几乎无用。而且实际上,SCAN VIP和SCAN VIP LISTENER的切换操作十分缓慢,在我的测试中relocate scan的操作大概需要花费2分钟才能完成,不确认是不是我个人的配置问题。

SCAN VIP也同样是绑定在RAC环境中的某个节点上。如下SCAN VIP则是绑定在dbserver2中的public网卡上,可以看到public网卡上总共有3个IP,一个是实IP,一个是VIP,一个是SCAN VIP。

# crs_stat -t|grep scan
ora....N1.lsnr ora....er.type ONLINE    ONLINE    dbserver2 
ora.scan1.vip  ora....ip.type ONLINE    ONLINE    dbserver2 

# srvctl config scan_listener
SCAN Listener LISTENER_SCAN1 exists. Port: TCP:1521

# srvctl config scan
SCAN name: crs-scan.cnrmall.com, Network: 1/192.168.255.0/255.255.255.0/en0
SCAN VIP name: scan1, IP: /crs-scan.cnrmall.com/192.168.255.250

# netstat -in
Name  Mtu   Network     Address           ZoneID    Ipkts Ierrs    Opkts Oerrs  Coll
en0   1500  link#2      0.21.5e.48.e4.60       -    96331     0    47140     0     0
en0   1500  192.168.255 192.168.255.225        -    96331     0    47140     0     0
en0   1500  192.168.255 192.168.255.235        -    96331     0    47140     0     0
en0   1500  192.168.255 192.168.255.250        -    96331     0    47140     0     0
en1   1500  link#3      0.21.5e.48.e4.61       -   342409     0   293503     0     0
en1   1500  172.16      172.16.0.2             -   342409     0   293503     0     0
lo0   16896 link#1                             -   103667     0   103678     0     0
lo0   16896 127         127.0.0.1              -   103667     0   103678     0     0
lo0   16896 ::1                                0   103667     0   103678     0     0

7. SCAN VIP在Oracle安装文档的建议中是需要配置在DNS服务器中,实际上也可以使用/etc/hosts文件,并且除却SCAN VIP之外的public ip、vip、private ip也仍然都是可以跟以前一样,配置在/etc/hosts文件中。

8. 安装11gR2 RAC要求必须配置ssh用户对等性,以前配置rsh的方式现在已经无法通过安装检查。OUI中提供了自动配置ssh用户对等性的按钮,因此无需再事先手动配置。

需要注意的是:该功能完全针对Linux环境进行的开发,因此在AIX环境中,需要事先作如下操作:

ln -s /usr/bin/ksh /bin/bash
mkdir -p /usr/local/bin
ln -s /usr/bin/ssh-keygen /usr/local/bin/ssh-keygen

在配置对等性时,OUI会使用/bin/bash,而AIX默认是没有bash的,因此需要将ksh软链接到bash(当然你也可以安装bash包)。
同样,OUI会使用/usr/local/bin/ssh-keygen产生对等性密钥,而AIX中在安装了OpenSSH以后,ssh-keygen命令默认是存储在/usr/bin中,因此也需要做link。

9. 在成功安装完Grid Infrastructure之后,运行cluvf命令可能会报错。

# cluvfy comp nodeapp -verbose

ERROR: 
CRS is not installed on any of the nodes
Verification cannot proceed

并且,在碰到这样的错误之后,也无法安装RAC,会碰到如下错误:

[INS-35354] The system on which you are attempting to install Oracle RAC is not part of a valid cluster.

也就是无论是cluvf命令还是OUI,都认为这个机器上没有安装CRS,并不是在一个集群环境中。但是实际上运行crsctl check crs命令是完全正常的。

这个错误的解决方法可以参看Metalink Note [ID 798203.1],大体上来说就是在安装Grid Infrastructure的时候,inventory.xml文件中丢掉了CRS=”true”字样,这无疑是安装程序的bug。需要手工detachHome再attachHome。

10. 11gR2 RAC在CRS资源部分做了很多改动,创建完RAC数据库以后的默认资源比以前多了不少。

# crs_stat -t
Name           Type           Target    State     Host        
------------------------------------------------------------
ora.CRSDG.dg   ora....up.type ONLINE    ONLINE    dbserver1   
ora.DATADG.dg  ora....up.type ONLINE    ONLINE    dbserver1   
ora.FRADG.dg   ora....up.type ONLINE    ONLINE    dbserver1   
ora....ER.lsnr ora....er.type ONLINE    ONLINE    dbserver1   
ora....N1.lsnr ora....er.type ONLINE    ONLINE    dbserver2   
ora.asm        ora.asm.type   ONLINE    ONLINE    dbserver1   
ora.dbcnr.db   ora....se.type ONLINE    ONLINE    dbserver2   
ora....SM1.asm application    ONLINE    ONLINE    dbserver1   
ora....R1.lsnr application    ONLINE    ONLINE    dbserver1   
ora....er1.gsd application    OFFLINE   OFFLINE               
ora....er1.ons application    ONLINE    ONLINE    dbserver1   
ora....er1.vip ora....t1.type ONLINE    ONLINE    dbserver1   
ora....SM2.asm application    ONLINE    ONLINE    dbserver2   
ora....R2.lsnr application    ONLINE    ONLINE    dbserver2   
ora....er2.gsd application    OFFLINE   OFFLINE               
ora....er2.ons application    ONLINE    ONLINE    dbserver2   
ora....er2.vip ora....t1.type ONLINE    ONLINE    dbserver2   
ora.eons       ora.eons.type  ONLINE    ONLINE    dbserver1   
ora.gsd        ora.gsd.type   OFFLINE   OFFLINE               
ora....network ora....rk.type ONLINE    ONLINE    dbserver1   
ora.oc4j       ora.oc4j.type  ONLINE    ONLINE    dbserver2   
ora.ons        ora.ons.type   ONLINE    ONLINE    dbserver1   
ora.scan1.vip  ora....ip.type ONLINE    ONLINE    dbserver2 

启动数据库实例以后,可以看到11gR2的后台进程已经增加到了43个,说实话,我很怀念简单的Oracle8i。

# ps -ef|grep ora_ | grep -v grep
  oracle  364656       1   0 17:01:17      -  0:00 ora_mark_dbcnr1 
  oracle  540722       1   0 17:01:17      -  0:03 ora_mmnl_dbcnr1 
  oracle  561184       1   0 18:07:34      -  0:00 ora_q003_dbcnr1 
  oracle  643244       1   0 17:01:17      -  0:01 ora_mmon_dbcnr1 
  oracle  651360       1   0 17:01:16      -  0:00 ora_asmb_dbcnr1 
  oracle  655494       1   0 17:01:16      -  0:00 ora_rbal_dbcnr1 
  oracle  663680       1   1 17:01:13      -  0:06 ora_lmd0_dbcnr1 
  oracle  667794       1   0 17:01:12      -  0:00 ora_pmon_dbcnr1 
  oracle  671832       1   0 17:01:12      -  0:01 ora_diag_dbcnr1 
  oracle  675932       1   0 17:01:16      -  0:00 ora_smon_dbcnr1 
  oracle  679962       1   0 17:01:12      -  0:00 ora_gen0_dbcnr1 
  oracle  696414       1   0 17:01:16      -  0:00 ora_dbw0_dbcnr1 
  oracle  708790       1   0 17:02:33      -  0:00 ora_qmnc_dbcnr1 
  oracle  716930       1   0 17:01:17      -  0:04 ora_lck0_dbcnr1 
  oracle  721124       1   0 17:01:16      -  0:00 ora_mman_dbcnr1 
  oracle  725186       1   0 17:02:32      -  0:00 ora_gtx0_dbcnr1 
  oracle  729102       1   0 17:01:15      -  0:00 ora_lmhb_dbcnr1 
  oracle  737358       1   0 17:01:16      -  0:00 ora_reco_dbcnr1 
  oracle  745554       1   0 17:02:34      -  0:00 ora_q001_dbcnr1 
  oracle  749762       1   0 17:01:16      -  0:00 ora_lgwr_dbcnr1 
  oracle  753716       1   0 17:01:12      -  0:00 ora_ping_dbcnr1 
  oracle  766014       1   0 17:01:13      -  0:00 ora_psp0_dbcnr1 
  oracle  790688       1   0 17:01:13      -  0:00 ora_acms_dbcnr1 
  oracle  794780       1   0 17:01:12      -  0:02 ora_vktm_dbcnr1 
  oracle  815252       1   0 17:01:12      -  0:00 ora_dbrm_dbcnr1 
  oracle  819350       1   1 17:01:15      -  0:16 ora_lms1_dbcnr1 
  oracle  827642       1   0 17:02:36      -  0:01 ora_cjq0_dbcnr1 
  oracle  848054       1   0 17:02:30      -  0:00 ora_arc0_dbcnr1 
  oracle  856270       1   0 17:01:15      -  0:00 ora_rms0_dbcnr1 
  oracle  868590       1   0 17:25:42      -  0:00 ora_q002_dbcnr1 
  oracle  872622       1   0 17:01:15      -  0:16 ora_lms0_dbcnr1 
  oracle  901314       1   0 17:02:32      -  0:00 ora_arc3_dbcnr1 
  oracle  921600       1   0 18:07:17      -  0:00 ora_pz98_dbcnr1 
  oracle  925926       1   0 17:01:18      -  0:00 ora_rsmn_dbcnr1 
  oracle  929980       1   0 17:07:35      -  0:00 ora_smco_dbcnr1 
  oracle  942286       1   0 18:07:17      -  0:00 ora_pz99_dbcnr1 
  oracle  950274       1   0 17:02:32      -  0:00 ora_rcbg_dbcnr1 
  oracle  958498       1   0 17:02:31      -  0:00 ora_arc2_dbcnr1 
  oracle  974876       1   0 18:07:38      -  0:00 ora_w000_dbcnr1 
  oracle 1011914       1   0 17:01:16      -  0:01 ora_ckpt_dbcnr1 
  oracle 1052884       1   1 17:01:13      -  0:06 ora_lmon_dbcnr1 
  oracle 1069246       1   1 17:01:13      -  0:33 ora_dia0_dbcnr1 
  oracle 1110056       1   0 17:02:31      -  0:00 ora_arc1_dbcnr1 
# ps -ef|grep ora_ | grep -v grep | wc -l
      43
#

ADRCI Purge is NOT What You Think It Should Be

ADRCI是在Oracle11g以后提供的实用程序,用来更加灵活的检查和分析各个ORACLE_HOME的告警文件,跟踪文件。

根据文档和帮助,ADRCI是具有purge功能的。

adrci> help purge

  Usage: PURGE [[-i  |  ] |
               [-age  [-type ALERT|INCIDENT|TRACE|CDUMP|HM|UTSCDMP]]]:

  Purpose: Purge the diagnostic data in the current ADR home. If no
           option is specified, the default purging policy will be used.

  Options:
    [-i id1 | id1 id2]: Users can input a single incident ID, or a
    range of incidents to purge.

    [-age ]: Users can specify the purging policy either to all
    the diagnostic data or the specified type. The data older than 
    ago will be purged

    [-type ALERT|INCIDENT|TRACE|CDUMP|HM|UTSCDMP]: Users can specify what type of
    data to be purged.

  Examples:
    purge
    purge -i 123 456
    purge -age 60 -type incident

仅仅看这个帮助,我们认为的ADRCI Purge功能是怎样的呢?比如我们执行了下面这个命令,那么Oracle应该将我们的告警日志中在距现在这个时刻1440分钟(也就是1天)以前的所有日志内容删除,是这样吧?可是确实是这样吗?

adrci> PURGE -age 1440 -type ALERT

我们测试一下。

adrci> show home
ADR Homes: 
diag/rdbms/orcl/orcl
diag/tnslsnr/dbserver/listener
adrci> set home diag/rdbms/orcl/orcl
adrci> show alert -tail
2010-05-02 20:04:56.447000 +08:00
Verifying file header compatibility for 11g tablespace encryption..
Verifying 11g file header compatibility for tablespace encryption completed
SMON: enabling tx recovery
Database Characterset is AL32UTF8
No Resource Manager plan active
2010-05-02 20:04:58.182000 +08:00
replication_dependency_tracking turned off (no async multimaster replication found)
2010-05-02 20:04:59.607000 +08:00
Starting background process QMNC
QMNC started with pid=18, OS id=1405 
2010-05-02 20:05:00.653000 +08:00
Completed: ALTER DATABASE OPEN
2010-05-02 20:05:07.033000 +08:00
Starting background process CJQ0
CJQ0 started with pid=19, OS id=1426 
2010-05-02 20:10:00.609000 +08:00
Starting background process SMCO
SMCO started with pid=20, OS id=1587 
2010-05-02 20:11:47.241000 +08:00
Thread 1 advanced to log sequence 16 (LGWR switch)
  Current log# 1 seq# 16 mem# 0: /app/oracle/oradata/orcl/redo01.log

现在告警日志中存在的某两次记录时间是2010-05-02 20:04:56和2010-05-02 20:11:47。我们准备删除掉20:04前后的告警信息,保留20:11的。

[oracle@dbserver alert]date
Sun May  2 20:14:20 CST 2010
[oracle@dbserver alert] pwd
/app/oracle/diag/rdbms/orcl/orcl/alert
[oracle@dbserver alert]$ ls -l
total 24
-rw-r----- 1 oracle dba 20925 May  2 20:11 log.xml

当前的时间是2010-05-02 20:14。检查了Oracle11g中告警日志的默认存储位置,最后的一次更新时间是20:11。

[oracle@dbserver alert]date
Sun May  2 20:15:51 CST 2010

adrci> PURGE -age 5 -type ALERT
adrci> show alert -tail
2010-05-02 20:04:56.447000 +08:00
Verifying file header compatibility for 11g tablespace encryption..
Verifying 11g file header compatibility for tablespace encryption completed
SMON: enabling tx recovery
Database Characterset is AL32UTF8
No Resource Manager plan active
2010-05-02 20:04:58.182000 +08:00
replication_dependency_tracking turned off (no async multimaster replication found)
2010-05-02 20:04:59.607000 +08:00
Starting background process QMNC
QMNC started with pid=18, OS id=1405 
2010-05-02 20:05:00.653000 +08:00
Completed: ALTER DATABASE OPEN
2010-05-02 20:05:07.033000 +08:00
Starting background process CJQ0
CJQ0 started with pid=19, OS id=1426 
2010-05-02 20:10:00.609000 +08:00
Starting background process SMCO
SMCO started with pid=20, OS id=1587 
2010-05-02 20:11:47.241000 +08:00
Thread 1 advanced to log sequence 16 (LGWR switch)
  Current log# 1 seq# 16 mem# 0: /app/oracle/oradata/orcl/redo01.log

[oracle@dbserver alert] ls -l
total 24
-rw-r----- 1 oracle dba 20925 May  2 20:11 log.xml

我们执行的命令成功了,但是很明显我们希望的目的没有达到,实际上,就好似没有执行这条命令一样,我们想要删除5分钟以前的内容,执行purge命令的时刻是20:15,那么理应在20:10之前的告警日志内容都被删除掉,但是实际上告警日志中20:04的内容依然还在,并且我们也可以看出log.xml文件的最后更新时间仍然是20:11。

那么purge命令到底有没有效果呢?

[oracle@dbserver alert]date
Sun May  2 20:28:34 CST 2010

adrci> PURGE -age 5 -type ALERT
adrci> show alert -tail
adrci> 

[oracle@dbserver alert] ls -l
total 0

在20:28的时候我们再次执行了purge命令,仍然是尝试删除5分钟以前的内容,这次的结果是所有的告警日志内容都被清空了,告警日志文件log.xml也被删除了。这条命令实际上有效果的,但是为什么不是我们期望的那样灵敏呢?为什么第一次没有成功,而第二次却成功了呢?

原因在于,ADRCI Purge命令的操作单元是文件而不是文件内容,只有整个文件的最后更新时间在我们指定的purge命令条件之前,该文件才会被删除,也就是说,要不整个文件都删除,要不一点儿也不删除。对于我们后一次执行的命令,因为文件的最后更新时间是20:11,这个时间点在我们执行purge命令时间点的5分钟以前,因此整个告警日志都被删除。而对于我们第一次执行的命令,因为告警日志中不但包含了执行purge命令时间点5分钟之前的内容,还包括了5分钟之前到执行purge命令时间点之间的内容(20:11的内容位于20:10和20:15之间),因此文件无法被删除。

无论是purge的什么TYPE,或者说purge -i 命令也同样是这样,对于INCIDENT来说,每个Incident ID在INCIDENT目录中都是一个子目录,因此很容易做到整个子目录的删除。

不得不说这样的purge命令并不是我们期望的,我也希望看到在今后的版本升级中ADRCI Purge可以变得如我们一开始期望的那样,直接删除符合条件的文件内容,而不是现在这样删除文件。

我们可以预见到对于特别繁忙的系统,假设每分钟都有告警日志内容生成,那么我们始终不可能执行成功purge,我们只能使用purge -age 0 -type ALERT这样的命令来清除所有的log内容。那么现在这样的purge命令我们该如何精确使用呢?

实际上仍然需要另外的客户化脚本,比如在crontab中定义每天晚上的某个时刻将现在的log.xml文件更名为log_`date`.xml,然后统一使用purge命令删除符合条件的告警日志文件备份。特别需要注意的是,更名规则要符合purge命令的检查规范,purge命令只会对相应目录中以log开头以xml结尾的文件进行检查。

PS:purge命令只会清除xml文件,对于trace目录中的和Oracle11g之前版本兼容的alert_.log文件并不会有任何改动。

[oracle@dbserver trace]pwd
/app/oracle/diag/rdbms/orcl/orcl/trace
[oracle@dbserver trace] ls -l alert*
-rw-r----- 1 oracle dba 41444 May  2 20:20 alert_orcl.log

RAC Object Remastering (Dynamic Remastering)

原文作者:Riyaj Shamsudeen
原文链接:http://orainternals.wordpress.com/2010/03/25/rac-object-remastering-dynamic-remastering/

译者注:如果您对本文章有兴趣,请一定先去阅读Riyaj的原文,在万不得已时可以使用这篇译文校对一下自己的理解。译文中的master, mastering, remastering, affinity lock都翻译的不满意,甚至有些词左思右想还是保留了原文。IT技术从业者应该以英文为母语。

感谢Yong Huang,从跟他的邮件往来中得到了很多帮助。

***********以下为译文全文***********。
RAC Object Remastering (Dynamic Remastering)
by Riyaj Shamsudeen

在RAC环境中,每个数据块都被一个实例所掌控(mastered)。掌控一个数据块意思是主实例(master instance)始终监控这个数据块的状态,直到下一次重配置事件发生(因为实例重启或者其它原因)。

Hash to the master
这些数据库是根据数据块范围来掌控的。举例来说,数据块范围从file 10,block 1开始到block 128可能是被实例1所掌控,而从file 10,block 129到block 256是被实例2所掌控。当然,在不同的版本10g,11g中都有所不同,只是中心思想都是将数据块均衡地分布在不同实例上由此Global Cache grants可以在各实例间平衡分布。有趣的是,从10g以后版本数据块范围的长度是128(Julian Dyke提到在9i中是1089,但是我个人还没有测试过)。技术支持推荐取消db_file_multiblock_read_count初始化参数的设置这样将会自动调整到128,我猜这意味着通过较少的GC消息就可以进行完整的多块读。走题了。

更深入的,Michael Möller指出这种哈希算法有进一步优化:当通过数据块地址(DBA)初次计算主节点时使用到哈希算法,生成一个“虚拟属主”,然后在通过一个查询表(长度是最大允许的节点数,128?)再转化为真实(在线且打开的)属主。这意味着当有一个节点关闭或者启动的时候,RAC不需要为所有数据块再次计算哈希值,而只需要分发新的Hash-to-node表。(这张表在节点发生变化时也同样需要重新分发)

以下SQL有助于显示数据块的属主(master)和拥有者(owner)。这个SQL结合了xkjbl和xle表来获得资源名称。如果你对于Oracle锁机制比较熟悉,你可能认识这些缓存融合(cache fusion)锁(以前的PCM锁)的结构。在这里锁类型是BL,id1是block#,id2是file_id。列kjblname2显示的是锁资源的十进制数字格式。

请注意下面的输出:
1. 数据块范围:File 1, block 6360-6376为node 3所掌控同时也属于node 3
2. 数据块范围:File 1,一直到10709数据块为实例0所掌控,属于实例3
3. 从10770开始的下一个数据块范围为实例2所掌控并且属于实例3

还请注意这些输出是从一个还没有remastering操作的数据库中获得的。

REM kjblname2列逗号前第一部分是数据块,第二部分file_id*65536是BL locks
[sourcecode language=”sql”]select kj.*, le.le_Addr from (
select kjblname, kjblname2, kjblowner, kjblmaster, kjbllockp,
substr ( kjblname2, instr(kjblname2,’,’)+1, instr(kjblname2,’,’,1,2)-instr(kjblname2,’,’,1,1)-1)/65536 fl,
substr ( kjblname2, 1, instr(kjblname2,’,’)-1) blk
from xkjbl
) kj, x
le le
where le.le_kjbl = kj.kjbllockp
order by le.le_addr
/

KJBLNAME KJBLNAME2 KJBLOWNER KJBLMASTER FL BLK LE_ADDR
———————- ————— ———- ———– — —– —————-
[0x18d8][0x10000],[BL] 6360,65536,BL 3 3 1 6360 000000038DF9AB08

[0x18e7][0x10000],[BL] 6375,65536,BL 3 3 1 6375 000000038DFBF818 => case #2
[0x18e8][0x10000],[BL] 6376,65536,BL 3 3 1 6376 000000038DFD3BA0

[0x29d1][0x10000],[BL] 10705,65536,BL 3 0 1 10705 00000005D6FE9230

[0x29d5][0x10000],[BL] 10709,65536,BL 3 0 1 10709 000000038EFBB990
[0x2a12][0x10000],[BL] 10770,65536,BL 3 2 1 10770 000000075FFE3C18

[0x2a18][0x10000],[BL] 10776,65536,BL 2 2 1 10776 000000038EFA8488 => case #1
[0x2a19][0x10000],[BL] 10777,65536,BL 3 2 1 10777 000000038EFB7F90
[0x2a1a][0x10000],[BL] 10778,65536,BL 1 2 1 10778 000000038EFCC318
[/sourcecode]

让我们看一下在实例3上运行3种简单SELECT语句的情况:
1. 一个会话尝试读取数据块file 1,block 10776,但是这个数据块被实例2掌控同时又被实例2拥有(也就是该块现在存在于实例2的buffer cache中)。所以,实例3将给实例2发送一个PR(Protected Read)模式的BL锁要求读取该块。更加复杂的情况我们先忽略掉,实例2将授予实例3一个PR模式锁并且将数据块传送给实例3。显然,这将会有多个GC消息、锁授予和块传递。统计值‘gc remote grants’也会增加。
2. 假设此会话尝试读取数据块file 1, block 6375。此块为实例3掌控同时被实例3拥有,那这时候就不会有额外的GCS/GES过程了,会话直接pin住buffer然后继续工作。
3. 考虑第三种情况。会话尝试读取数据块file 1 block 6374。此块不在任何实例的buffer cache中,但是实例3是这个块的属主,所以只需要消耗少量GC消息和等待而获得本节点的affinity locks,然后将数据块从磁盘读到缓存中来。
在上面的情况2和情况3中,请求者实例同时也是数据块或者块范围的属主节点。在这种情况下,统计值‘gc local grants’增加,这些块上获得的本节点的affinity locks代价较低,这样避免了大量的全局缓存消息。

目前为止一切都很好,但是如果有这样一种情况,实例1在频繁读取一个对象(表、索引等),而其他实例则完全不读取该对象呢(某些应用的代码分区策略或者负载策略就会产生这种现象)?频繁读取一个对象的实例需要为每次OPEN该对象数据块的操作向远程实例请求授权,这合理吗?特别是,当数据块被读进buffer cache,但是又很快从buffer cache中消失的时候呢?如果说频繁读取某个对象的实例又恰恰是这个对象的属主,就像我们在情况2和情况3中看到的那样,这岂不是更好吗?
除此之外,如果数据块即将从缓冲池中移走或者即将被覆盖,就会导致在主实例和其它实例之间的额外消息开销,因为所有权信息需要再被送回块的属主处。

进入对象remastering主题。

Object Remastering
在10g/11g RAC中有很多新功能。其中一个就是对象remastering。这个特性在10gR1中引入,10gR2中得到提高,11g中的得到进一步增强。我注意到其实在9i中也有相应的参数,不过我认为实际上并不能正常工作。
有了对象remastering特性,如果一个对象被某个实例频繁读取,那么这个实例将会成为这个对象的master以此来减少gc remote grants从而提高整个应用的性能。在前面这句话中我用到了“读取(accessed)”这个词,但是实际上并不太贴切,更准确的说法是如果某个实例请求了一个对象上的很多BL锁,那么这个对象将remastered。在完美世界中,即使应用程序没有完善的分区策略,remastering被某个实例频繁读取的对象这样的举措也会因为获取的是更廉价的本地实例亲和锁(affinity locks),从而使RAC开销降到最小。

好吧,我也说了是在完美世界中。实际上这里有几个问题:
1. 重启以后实例不会记住之前的属主情况。这意味着每次重新启动实例都需要重新学习对象的属主情况图。我能预计想要记住属主情况是比较复杂的,但是我认为还是有可能实现的。
2. Remastering的代价并不低。在重配置时实例GRD会被冻结(frozen),而繁忙时这个操作可能需要几秒钟,这将会导致整个实例在这几秒内被冻结。虽然10gR2推出了并行重配置(parallel reconfiguration)(通过_rcfg_parallel_replay初始化参数控制)使用所有LMS进程来完成重配置,但是几秒钟的系统冻结在很多环境中还是完全不能被接受的。
3. 我建议我的客户将LMS进程保持在较低的个数(最多3-5),但是如果降低LMS进程数目,那么实例重配置的有效并行度也会降低。
4. 最后,但并不是最不重要的,对于较繁忙的环境来说,触发remastering事件的默认参数值通常过低,因此导致了频繁的对象remastering。在EBS环境中,对于manager的配置稍稍处理不善就可能导致大量的重配置问题。

Parameters, views and internals
控制remastering行为的有几个参数,并没有很好的文档说明。我的测试结果也并不是非常精确。但是,这些参数还是给了我们一个内部到底如何运作的大体概貌。这些在10gR2版本以前是有效的。而对于11g,整套参数都不一样了,我将另写一篇文章来描述这些改变。

x$object_affinity_statistics维护对象和对象上的OPENs的统计信息。理解OPEN和Buffer Access之间的区别是重要的。如果缓存中的数据块已经处于适当的模式,就没有必要在数据块上打开BL锁,所以如果会话反复存取同一个数据块而不请求额外的BL锁,那么计数就不会增加,因此所谓OPEN就是某一刻的时间帧内对于BL锁的请求计数。

LCK0进程维护着这些对象affinity的统计信息。如果一个实例在比另一实例在某一对象上多打开了50个open(由_gc_affinity_limit参数控制)(译者注:实际上在超过2节点的RAC中,该参数的意思是如果一个实例比环境中其它所有实例访问某一个对象的总和还要多50),那么该对象将成为remastering候选者。该对象被放入队列,LMD0进程读取队列,开始GRD冻结。LMON进程协同LMS进程一起执行buffer cache locks的重配置。所有的这些动作都可以在LMD0/LMON trace文件中找到。初始化参数_gc_affinity_time控制队列的检查间隔,以确认remastering是否要触发,默认值是10分钟。

现在,你不愿看到无论哪个对象都能成为候选者而发生remastering,也就是说,如果仅仅是实例1在某对象上打开了101个BL锁而实例2打开了50个BL锁,那么你不想触发该对象的remastering。只有那些有很高的BL锁请求的对象才应该被放入队列并被remastering。此阀值被另一个参数_gc_affinity_minimum控制,只有超过该参数定义的 “每分钟动态affinity活动的最小数量”的对象才会成为候选者而发生remastering。默认值是2500,我认为这在繁忙的系统中有些低。(译者注:在11g中,默认值是1500,更低)
LMD0 trace文件中的以下几行表示LMD0进程正在读取请求队列:

* kjdrchkdrm: found an RM request in the request queue
  Transfer pkey 6589904 to node 3
*** 2009-10-12 11:41:20.257

How bad can it get?
Remastering问题发生时会影响性能。以下AWR报告显示了DRM重配置问题导致的实例冻结。同样类型的冻结在其它的所有节点上也都可以看见。gc buffer busy等待只是DRM冻结的副作用(不是总有,但是在这个case是这样)。

Top 5 Timed Events                                         Avg %Total
~~~~~~~~~~~~~~~~~~                                        wait   Call
Event                                 Waits    Time (s)   (ms)   Time Wait Class
------------------------------ ------------ ----------- ------ ------ ----------
gc buffer busy                    1,826,073     152,415     83   62.0    Cluster
CPU time                                         30,192          12.3
enq: TX - index contention           34,332      15,535    453    6.3 Concurrenc
gcs drm freeze in enter server       22,789      11,279    495    4.6      Other
enq: TX - row lock contention        46,926       4,493     96    1.8 Applicatio

在同一时刻,DRM大量发生,导致了重复的DRM冻结、实例重配置,从而引发严重的性能问题。

* received DRM start msg from 2 (cnt 5, last 1, rmno 404)
Rcvd DRM(404) Transfer pkey 1598477 from 3 to 2 oscan 1.1
Rcvd DRM(404) Dissolve pkey 6100030 from 2 oscan 0.1
Rcvd DRM(404) Dissolve pkey 5679943 from 2 oscan 0.1
Rcvd DRM(404) Transfer pkey 6561036 from 0 to 2 oscan 1.1
Rcvd DRM(404) Transfer pkey 5095243 to 2 oscan 0.1
...

A small test case
我们来看一个测试,观察一下DRM的产生。使用了索引读路径的查询从一个大索引中读取了几乎所有数据块。
[sourcecode language=”sql”]Session #1:
select data_object_id from dba_objects where object_name=’WMS_N1′;
DATA_OBJECT_ID
————-
6099472

REM 还没有affinity统计信息
select * from xobject_affinity_statistics where object=6099472;
no rows selected.

REM 执行高代价的查询语句
select /*+ index(a WMS_N1) */ count(*) from big_table1 a;

Session #2: 观察DRM表:
REM DRM字段现在是409,。我们将持续观察这个数值。在这个视图中还有其它比较有趣的字段。
select drms from XKJDRMAFNSTATS;
DRM
—-
409

REM 观察到自从会话#1开始运行在该索引上已经有23442个OPENs
select * from xobject_affinity_statistics where object=6099472;
ADDR INDX INST_ID OBJECT NODE OPENS
—————- ———- ———- ———- ———- ———-
FFFFFFFF7C05BFA8 14 1 6099472 1 23442

REM 该对象上还没有发生mastering
select * from vgcspfmaster_info where object_id=6099472;
no rows selected

REM 几秒后,OPENs计数从23442上升到33344
select * from xobject_affinity_statistics where object=6099472;
ADDR INDX INST_ID OBJECT NODE OPENS
—————- ———- ———- ———- ———- ———-
FFFFFFFF7C05BFA8 16 1 6099472 1 33344

REM该对象上还没有发生mastering
select * from vgcspfmaster_info where object_id=6099472;
no rows selected

REM 让人诧异,会话#1还在运行,OPENs计数忽然清零即使DRM还未发生过
REM OPENs从0又升到1229因为会话#1还在运行
ADDR INDX INST_ID OBJECT NODE OPENS
—————- ———- ———- ———- ———- ———-
FFFFFFFF7C05BFA8 0 1 6099472 1 1229

REM 大约10分钟以后,remastering发生了。DRMS从409上升到411。
select drms from XKJDRMAFNSTATS;
DRM
—-
411

REM 该索引的remastering发生了。现在该对象的属主是0,也就是实例1
select * from vgcspfmaster_info where object_id=6099472;
FILE_ID OBJECT_ID CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
———- ———- ————– ————— ————
0 6099472 0 32767 1

REM OPENs还在上升,但是remastering已经发生了。
select * from xobject_affinity_statistics where object=6099472;
ADDR INDX INST_ID OBJECT NODE OPENS
—————- ———- ———- ———- ———- ———-
FFFFFFFF7C05AF78 2 1 6099472 1 42335
FFFFFFFF7C05AF78 3 1 6099472 2 1

REM LMON trace文件也指出pkey的传递。注意pkey和object_id是相同的。
*** 2010-03-23 10:41:57.355
Begin DRM(411) – transfer pkey 6099472 to 0 oscan 0.0
ftd received from node 1 (4/0.30.0)
all ftds received

REM 几秒后,OPENs被再次重置。
select * from xobject_affinity_statistics where object=6099472;
ADDR INDX INST_ID OBJECT NODE OPENS
—————- ———- ———- ———- ———- ———-
FFFFFFFF7C05BFA8 0 1 6099472 1 7437

REM 属主仍然是实例1。
select * from v$gcspfmaster_info where object_id=6099472;
FILE_ID OBJECT_ID CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
———- ———- ————– ————— ————
0 6099472 0 32767 1[/sourcecode]

我们的测试表明,该索引上发生了大量的BL锁请求,之后对象被remastering。

Undo and affinity
回滚段的Mastering和其它段不同。对于非回滚段而言,一个段上的所有数据块通过哈希算法被分散在各个实例间。只有在经过大量的BL锁请求以后,段才会被mastering。但是对于回滚段而言,激活了一个回滚段的实例立刻成为该段的属主。这是合乎情理的,因为在大多数情况下回滚段将会被打开这个segment的实例使用。初始化参数_gc_undo_affinity控制这种动态undo remastering动作是否发生。

因为回滚段没有真正的object_id,所以使用4294950912作为虚拟object_ids的基准值。比如说,回滚段1(usn=1)的object_id是4294950913,回滚段2(usn=2)的object_id就是4294950914,依次类推(4294950912 = power(2,32) – power (2,14) = xFFFFC000)。

[sourcecode language=”sql”]select objecT_id, object_id-4294950912 usn, current_master, previous_master,
remaster_cnt from vgcspfmaster_info where object_id>4294950912;

OBJECT_ID USN CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
———- ———- ————– ————— ————
4294950913 1 0 32767 1
4294950914 2 0 32767 1
4294950915 3 0 32767 1
4294950916 4 0 32767 1
4294950917 5 0 32767 1
4294950918 6 0 32767 1
4294950919 7 0 32767 1
4294950920 8 0 32767 1
4294950921 9 0 32767 1
4294950922 10 0 32767 1
4294950923 11 1 32767 1
4294950924 12 1 32767 1
4294950925 13 1 32767 1

REM 注意usn 0在两个实例中都存在,这是系统回滚段。
REM 下面结果显示,头10个回滚段被node1掌控,而接下来的3个被实例2掌控。
select inst_id, usn, gets from gvrollstat where usn <=13 order by inst_id, usn;
INST_ID USN GETS
———- ———- ———-
1 0 3130
1 1 108407
1 2 42640
1 3 43985
1 4 41743
1 5 165166
1 6 43485
1 7 109044
1 8 23982
1 9 39279
1 10 48552
2 0 4433
2 11 231147
2 12 99785
2 13 1883[/sourcecode]

我没有成功试验出触发下一次回滚段remastering。我创建了一个活动事务在一个节点上产生了200K回滚数据块,然后另外一个节点在读这个表,我观察到在回滚数据块上有大量等待。但是我没有发现这个回滚段上有任何remastering事件。无法确认为什么没有产生,也许是回滚段remastering的条件有所不同吧。

译者注:回滚段的remastering是不会因为另外一个节点对于回滚段有大量读取而发生的,只有在某个实例失效,然后负责进行实例恢复的另外那个实例会暂时的成为这些回滚段的master,这是为了进行实例恢复的需要,在恢复完毕以后所有的undo buffers都会被flush到磁盘上。

PS:我能够使用后面将描述的lkdebug命令手动remaster回滚段,所以oracle也应该可以自动remaster回滚段,只是可能还有一个其它条件也必须满足。

[sourcecode language=”sql”]select * from v$gcspfmaster_info where object_id=431+4294950912;
FILE_ID OBJECT_ID CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
———- ———- ————– ————— ————
0 4294951343 0 32767 1

Oradebug lkdebug –m pkey 4294951343[/sourcecode]

* kjdrchkdrm: found an RM request in the request queue
  Transfer pkey 4294951343 to node 1
*** 2010-03-24 12:47:29.011
Begin DRM(520) - transfer pkey 4294951343 to 1 oscan 0.1
 ftd received from node 0 (4/0.31.0)
 all ftds received

[sourcecode language=”sql”]select * from v$gcspfmaster_info where object_id=431+4294950912;
SQL> /
FILE_ID OBJECT_ID CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
———- ———- ————– ————— ————
0 4294951343 1 0 2[/sourcecode]

我不是在劝你们应该去修改这些隐含参数。只是要去理解这些参数,如果你们碰到诸如’gc remaster’, ‘gcs freeze for instance reconfiguration’这样的等待事件,那么应该知道是不是因为默认值太低了。跟技术支持沟通尝试是否能够调整。

Manual remastering
你可以使用oradebug命令来手动remaster一个对象:
[sourcecode language=”sql” light=”true”]oradebug lkdebug -m pkey[/sourcecode]

这会将一个对象remaster请求放入队列。LMD0和LMON进程会完成这个请求。

*** 2010-01-08 23:25:54.948
* received DRM start msg from 1 (cnt 1, last 1, rmno 191)
Rcvd DRM(191) Transfer pkey 6984154 from 0 to 1 oscan 0.0
 ftd received from node 1 (8/0.30.0)
 ftd received from node 0 (8/0.30.0)
 ftd received from node 3 (8/0.30.0)

当前属主是从0开始计数的。

[sourcecode language=”sql”]select * from vgcspfmaster_info where object_id=6984154;
SQL> /
FILE_ID OBJECT_ID CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
———- ———- ————– ————— ————
0 6984154 1 0 2

SQL> oradebug lkdebug -m pkey 6984154
Statement processed.

SQL> select * from vgcspfmaster_info where object_id=6984154;

FILE_ID OBJECT_ID CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
———- ———- ————– ————— ————
0 6984154 2 1 3[/sourcecode]

Summary
总结一下,remarstering是个很棒的功能。不过遗憾的是,我们有时候会成为它负面效果的受害者。所以,如果你碰到remastering引起的问题,不要直接就禁用它,而是应该去看看能否调优那些参数从而控制remastering事件。如果你仍然想完全禁用DRM,那么我建议设置_gc_affinity_limit和_gc_affinity_minimum参数到一个较高值,比如说1千万。将参数_gc_affinity_time设置为0也可以完全禁用DRM,但是这也意味着再也无法手工remaster对象了。另外,Arup也提到如果DRM被禁用那么x$object_affinity_statistics表也不会再被维护。

再次提醒,这些是隐含参数。在你修改这些参数之前确认Oracle Support同意你的举动。

Update 1:
从11g开始,affinity管理更名为policy管理(策略管理)。比如说,xobject_affinity_statistics表改名为xobject_policy_statistics,与之相似的,初始化参数也发生了改变:参数_gc_affinity_limit改名为_gc_policy_limit;参数_gc_affinity_time改名为_gc_policy_time;出现了一个新的视图vpolicy_history,其中所有policy_event = ‘initiate_affinity’的记录都是与DRM事件相关的。
本blog的其它部分仍然没问题,除了默认的_gc_policy_limit参数值降低为1500,这意味着,在理论上,11g可能会产生更多的DRM事件。
[sourcecode language=”sql”] 1* select * from v
policy_history
INST_ID POLICY_EVENT DATA_OBJECT_ID TARGET_INSTANCE_NUMBER EVENT_DATE
———- ——————– ————– ———————- ——————–
2 glru_on 0 1 10/15/2010 10:58:28
2 glru_on 0 1 10/15/2010 11:21:32
2 initiate_affinity 74809 1 10/15/2010 13:27:44
[/sourcecode]

【非常感谢Arup NandaMichael Möller(M2)校对这篇blog,他们大量参与了我的讨论。但是,如果这篇文章中有什么错误那么全是我个人的责任。】
这篇文章的pdf格式可以此Dynamic_remastering_RAC_orainternals下载.