Oracle10g/11g RAC数据库中的Master实例、Owner实例和PastImage的概念
×××ARP项目实施顾问,上海Oracle用户组成员 唐波
摘要
本文探索Oracle RAC数据库的内部原理。探索Oracle RAC数据库的调优源头,弄清“Master实例”、“Owner实例”和“Past Image”的概念。
目录
1.Oracle RAC数据库服务器实例间的网络通信
2.Oracle RAC数据库服务器中最重要的内存结构:GRD和GRD中的资源
3.数据块上的BL锁和PR授权
4.能够查出某个块Master实例和Owner实例的X$表
5.自动Remaster(Object Affinity andDynamic Remastering引起)和手工Remaster(oradebug命令)
6.实例恢复过程中的Remaster(DynamicReconfiguration)
总结
正文
1. Oracle RAC数据库服务器实例间的网络通信
下图描述了Oracle RAC数据库服务器实例间内连网通信体系结构(见图1):
图1:Oracle RAC数据库服务器实例间内连网通信体系结构示意图
Oracle RAC数据库服务器调优的重要目标就是尽量减少实例间没有必要的内连网通信量。下节所讨论的“Oracle RAC数据库服务器中最重要的内存结构:GRD和GRD中的资源”,都是通过内连网通信来维护。
2.Oracle RAC数据库服务器中最重要的内存结构:GRD和GRD中的资源
在RAC的体系结构中(见图2),全局资源目录(Global ResourceDirectory简称GRD)是Oracle RAC数据库服务器中最重要的内存结构。它是一套哈希分布于各个实例间由被称为“gcs mastership buckets”、“gcs res hash bucket”和“gcs resources”等内存结构组成的的元数据集(见表1)。这个哈希分布元数据集用以描述Oracle RAC数据库服务器中数据块的状态、属主信息以及数据块内部和数据块自身的锁信息。GRD分布在所有实例的共享池中,每个实例维护GRD的一部份。所有实例维护的GRD合起来形成哈希分布式的整体集。GRD内部包含“转换队列”和“写队列”,这两个队列被GCS(Global Cache Service)和GES(Global EnqueueService)维护。所有维护信息通过前面介绍过的内连网传输。
图2:Oracle RAC体系结构图
POOL | NAME | BYTES | |
1 | shared pool | KCL buffer header | 9664 |
2 | shared pool | KCL instance cache transf | 262144 |
3 | shared pool | KCL lock contexts | 14420 |
4 | shared pool | KCL lock state | 3584 |
5 | shared pool | KCL name table | 262144 |
6 | shared pool | KCL offline scn array | 1608 |
7 | shared pool | KCL partition table | 720896 |
8 | shared pool | KCL region array | 8 |
9 | shared pool | gcs I/O statistics struct | 32 |
10 | shared pool | gcs affinity | 4108 |
11 | shared pool | gcs close obj | 4104 |
12 | shared pool | gcs commit sga state | 67596 |
13 | shared pool | gcs mastership buckets | 4608 |
14 | shared pool | gcs opaque in | 4028 |
15 | shared pool | gcs res hash bucket | 32768 |
16 | shared pool | gcs res latch table | 15360 |
17 | shared pool | gcs resource freelist arr | 272 |
18 | shared pool | gcs resource freelist dyn | 32 |
19 | shared pool | gcs resources | 4812504 |
20 | shared pool | gcs scan queue array | 216 |
21 | shared pool | gcs shadow locks dyn seg | 32 |
22 | shared pool | gcs shadow locks freelist | 272 |
23 | shared pool | gcs shadows | 3418328 |
表1:共享池中的与RAC相关内存结构,数据来源于:V$SGASTAT
理论上每个数据块都有对应于它的GRD信息:无论它当前存在于某个实例的数据库缓冲区缓存中还是已经被写回硬盘数据文件中。这些信息加上管理这些信息的锁合在一起称为“资源”。
既然GRD是分布式的整体,对于一个数据块而言,管理该数据块的状态和属主信息以及数据块内部和数据块自身的锁信息的实例只有一个。这个实例就被称作为该数据块(或更准确地说:资源)的Master实例。Oracle这样做是为了将数据块状态和属主等信息均衡地哈希分布在不同实例上。从10g以后版本开始,数据块的状态和属主等信息被存储成每128个块的信息一个master单元,即128个数据块的状态和属主等信息构成一个“gcs mastership bucket”。但是要说明以下:一个“gcsmastership bucket”不一定要存满128个块的状态和属主等信息。这样就能理解:超过128个块的表的数据块可以被多个实例分布式地分段master。如果发生自动Remaster(Object Affinity and Dynamic Remastering引起)或手工Remaster(oradebug命令),整个对象将作为master单元而不进行多个实例分布式地分段master:即不管表多大,它的数据块都由同一个实例master。另外,任何时候undo段整段必需由同一个实例master。
数据库缓冲区缓存中拥有某个数据块内存拷贝的实例被称作该数据块的Owner实例。Owner实例的个数可以是0(最小)到集群节点总数(最大)中的任何数值。如果该数据块内存拷贝在多个实例的数据库缓冲区缓存中同时被找到,也就是说该数据块有多个Owner实例,那么证明在近期有多个实例先后修改或访问过它。在所有这些该数据块的内存拷贝中,SCN最大的那个内存拷贝被称为Current Image(XI),SCN不是最大的那些内存拷贝就都被统统称为该数据块的 PastImage(PI)。PI的存在主要是为了在实例恢复过程中能被利用来减少实例交叉恢复的时间。如果由于检查点事件XI被写回硬盘,那么所有它所对应的所有PI都将被从内存中直接flush掉。还有一点值得注意的是:XI和PI都可以包含各自的new value和old value。通常old value在Oracle文档中又被称为Before Image即:BI。
最后说一下:对于回滚段上的BI而言,激活了一个回滚段的实例立刻成为该段的master实例。因为该回滚段将会被打开这个回滚段的实例所使用,用以写入事务产生的BI。因为回滚段没有真正的object_id,所以在X$KJBL表中Oracle使用“4294950912+回滚段号”作为该回滚段的object_id。
以下用一幅图来形象描绘了一下数据库服务器其中一个实例的GRD构成(见图3):
图3:某个实例的GRD构成示意图(其中:Null表示空锁,Shared表示共享锁,Exclusive表示独占锁)
3.数据块上的BL锁和PR授权
对于RAC,Buffer cache中的Buffer以及硬盘数据文件上的块,都是锁管理的对象之一。这种锁就被称为“BL锁”,单实例的数据库上没有这种类型的锁。对Buffer和硬盘数据文件上的块的修改或访问都要先得到它的master实例的“Protected Read”授权,简称PR授权(锁状态为:KJUSERPR)。PR授权就是获得BL锁的过程。下面列出查看BL锁和PR请求的SQL语句(见表2),同时形象展现GRD中的BL锁和PR请求的动态过程(见图4)。如前所述BL锁和被其锁住的信息合在一起就是一个BL锁资源。
select* from v$ges_resourcewhere resource_name like '%BL%'; select* from v$ges_enqueuewhereresource_name2 like '%BL%'; 注:v$ges_resource和v$dlm_ress是相同的。 |
表2:查看BL锁资源和PR请求的SQL语句
图4:Buffer Cache中的数据块变化前必需询问GRD
某个实例是否有申请了BL锁资源,关键是理解该实例对数据块Open和Buffer Access之间的区别。如果缓存中的数据块已经处于适当的模式,就没有必要在数据块上再打开BL锁。所以如果会话反复存取同一个数据块而不请求额外的BL锁,那么在X$OBJECT_AFFINITY_STATISTICS中BL锁的Open计数就不会增加。这个计数可以从以下的查询语句中查出(见表3)。
实验第1步: 查看实验用表t04209_uname的定义 在任一个实例上执行: select t.TABLE_NAME,t.COLUMN_NAME,t.DATA_LENGTH,t.DATA_PRECISION from dba_tab_columns t where t.owner='HR' and t.table_name='T04209_UNAME';
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第2步: 查看实验用表t04209_uname的内容 在任一个实例上执行: select* from hr.t04209_uname;
共10万行 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第3步: 查看实验用表t04209_uname的数据块个数 在任一个实例上执行: selects.segment_name,s.blocksfrom dba_segments s where s.owner='HR' and s.segment_name='T04209_UNAME';
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第4步: 查看实验用表t04209_uname的object_id 在任一个实例上执行: selecto.object_name, o.object_idfrom dba_objects o where o.owner='HR' and o.object_name='T04209_UNAME';
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第5步: 在任一个实例上执行: select* from x$OBJECT_AFFINITY_STATISTICS where object=52533; 无输出 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第6步: 在实例1上update这个表t04209_uname: update hr.t04209_uname set uvalue=2 where uname='a1'; update hr.t04209_uname set uvalue=3 where uname='a2'; update hr.t04209_uname set uvalue=4 where uname='a3'; update hr.t04209_uname set uvalue=5 where uname='a4'; update hr.t04209_uname set uvalue=6 where uname='a5'; update hr.t04209_uname set uvalue=7 where uname='a6'; update hr.t04209_uname set uvalue=8 where uname='a7'; …… update hr.t04209_uname set uvalue=99996 where uname='a99995'; update hr.t04209_uname set uvalue=99997 where uname='a99996'; update hr.t04209_uname set uvalue=99998 where uname='a99997'; update hr.t04209_uname set uvalue=99999 where uname='a99998'; update hr.t04209_uname set uvalue=100000 where uname='a99999'; update hr.t04209_uname set uvalue=100001 where uname='a100000'; 共10万行 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第7步: 在任一个实例上执行: select* from x$OBJECT_AFFINITY_STATISTICS where object=52533;
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第8步: 等实例1update结束后,再查x$OBJECT_AFFINITY_STATISTICS没有输出。 重新在实例1上做 update,x$OBJECT_AFFINITY_STATISTICS仍然没有输出。 这就说明:如果缓存中的数据块已经处于适当的模式,就没有必要在数据块上再打开BL锁。所以如果会话反复存取同一个数据块而不请求额外的BL锁,那么在x$OBJECT_AFFINITY_STATISTICS中BL锁的Open计数就不会增加。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第9步: 在实例1上Alter system flush buffer_cache后,再做同样的update。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第10步: 在任一个实例上执行: select* from x$OBJECT_AFFINITY_STATISTICS where object=52533;
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第11步: 为了使情况更复杂点,在实例2同时进行(保证实例1还在执行刚才的update): update hr.t04209_uname set uvalue=9 where uname='a8'; update hr.t04209_uname set uvalue=99 where uname='a98'; update hr.t04209_uname set uvalue=999 where uname='a998'; update hr.t04209_uname set uvalue=9999 where uname='a9998'; update hr.t04209_uname set uvalue=99999 where uname='a99998'; update hr.t04209_uname set uvalue=99998 where uname='a99997'; update hr.t04209_uname set uvalue=99997 where uname='a99996'; update hr.t04209_uname set uvalue=99996 where uname='a99995'; update hr.t04209_uname set uvalue=99995 where uname='a99994'; …… update hr.t04209_uname set uvalue=10006 where uname='a10005'; update hr.t04209_uname set uvalue=10005 where uname='a10004'; update hr.t04209_uname set uvalue=10004 where uname='a10003'; update hr.t04209_uname set uvalue=10003 where uname='a10002'; update hr.t04209_uname set uvalue=10002 where uname='a10001'; update hr.t04209_uname set uvalue=10001 where uname='a10000'; update hr.t04209_uname set uvalue=10000 where uname='a9999'; update hr.t04209_uname set uvalue=100001 where uname='a100000'; update hr.t04209_uname set uvalue=100000 where uname='a99999'; 共10万行,这10万行update是不按顺序打乱后的组合。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第12步: 在任一个实例上执行: select* from x$OBJECT_AFFINITY_STATISTICS where object=52533;
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第13步: 在任一个实例上执行: selectto_char(52533,'xxxxxxxx')from dual;
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第14步: 在任一个实例上执行: 查看两个实例此时的资源状态 selectinst_id ,resource_name,on_convert_q, on_grant_q,master_node, next_cvt_levelfrom gv$ges_resourcewhereresource_name like'[0xcd35]%';
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
实验第15步: 在任一个实例上执行: 查看两个实例此时的锁状态 selectinst_id,grant_level,request_level,resource_name2,PID , TRANSACTION_ID0, TRANSACTION_ID1 from gv$ges_enqueue whereresource_name2 like '52533%';
KJUSERPR是BL锁,KJUSERCW是表级共享锁,KJUSERTM是行级独占锁,KJUSERNL是空锁,KJUSEREX是lock table之类命令产生的独占锁 |
转载于:https://blog.51cto.com/botang/1351714