Oracle10g/11g RAC数据库中的Master实例、Owner实例和PastImage的概念


中国科学院ARP项目实施顾问,上海Oracle用户组成员 唐波



摘要


本文探索Oracle RAC数据库的内部原理。探索Oracle RAC数据库的调优源头,弄清“Master实例”、“Owner实例”和“Past Image”的概念。






目录


1Oracle RAC数据库服务器实例间的网络通信

2Oracle RAC数据库服务器中最重要的内存结构:GRDGRD中的资源

3.数据块上的BL锁和PR授权

4.能够查出某个块Master实例和Owner实例的X$

5自动Remaster(Object Affinity andDynamic Remastering引起)手工Remaster(oradebug命令)

6.实例恢复过程中的RemasterDynamicReconfiguration

总结


正文


1. Oracle RAC数据库服务器实例间的网络通信

下图描述了Oracle RAC数据库服务器实例间内连网通信体系结构(见图1):


wKiom1LV9CryywGkAAGfFohC6Q4973.jpg

1Oracle RAC数据库服务器实例间内连网通信体系结构示意图


Oracle RAC数据库服务器调优重要目标就是尽量减少实例没有必要的内连网通信量。下节所讨论的“Oracle RAC数据库服务器中最重要的内存结构:GRDGRD中的资源”,都是通过内连网通信来维护。


2Oracle RAC数据库服务器中最重要的内存结构:GRDGRD中的资源


在RAC的体系结构中(见图2),全局资源目录(Global ResourceDirectory简称GRD)是Oracle RAC数据库服务器中最重要的内存结构。它是一套哈希分布于各个实例间由被称为“gcs mastership buckets、“gcs res hash bucket”和“gcs resources”等内存结构组成的的元数据集(见表1)。这个哈希分布元数据集用以描述Oracle RAC数据库服务器中数据块的状态、属主信息以及数据块内部和数据块自身的锁信息。GRD分布在所有实例的共享池中,每个实例维护GRD的一部份。所有实例维护的GRD合起来形成哈希分布式的整体集。GRD内部包含“转换队列”和“写队列”,这两个队列被GCSGlobal Cache Service)和GESGlobal EnqueueService)维护。所有维护信息通过前面介绍过的内连网传输。


wKiom1LV9EiQvXpGAAOlLJB8OBQ701.jpg

图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掉。还有一点值得注意的是:XIPI都可以包含各自的new value和old value。通常old value在Oracle文档中又被称为Before Image即:BI。

最后说一下:对于回滚段上的BI而言,激活了一个回滚段的实例立刻成为该段的master实例。因为该回滚段将会被打开这个回滚段的实例所使用,用以写入事务产生的BI。因为回滚段没有真正的object_id,所以在X$KJBL表中Oracle使用“4294950912+回滚段号”作为该回滚段的object_id。

以下用一幅图来形象描绘了一下数据库服务器其中一个实例的GRD构成(见图3):


wKioL1LV9E-SPyqqAAGjr-k8af4309.jpg

图3:某个实例的GRD构成示意图(其中:Null表示空锁,Shared表示共享锁,Exclusive表示独占锁)


3.数据块上的BL锁和PR授权

对于RACBuffer 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_resourcev$dlm_ress是相同的。

2:查看BL锁资源和PR请求的SQL语句



wKiom1LV9G6S-EPkAADiToeyCUg047.jpg

4Buffer Cache中的数据块变化前必需询问GRD


某个实例是否有申请了BL锁资源,关键是理解该实例对数据块OpenBuffer Access之间的区别。如果缓存中的数据块已经处于适当的模式,就没有必要在数据块上再打开BL锁。所以如果会话反复存取同一个数据块而不请求额外的BL锁,那么在X$OBJECT_AFFINITY_STATISTICSBL锁的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';



TABLE_NAME

COLUMN_NAME

DATA_LENGTH

DATA_PRECISION

1

T04209_UNAME

UNAME

60


2

T04209_UNAME

UVALUE

22

9




实验第2步:

查看实验用表t04209_uname的内容

在任一个实例上执行:

select* from hr.t04209_uname;


UNAME

UVALUE

1

a1

1

2

a2

2

3

a3

3

4

a4

4

5

a5

5

6

a6

6

7

a7

7

8

a8

8

9

a9

9

10

a10

10

…...

…...

…...

10万行



实验第3步:

查看实验用表t04209_uname的数据块个数

在任一个实例上执行:

selects.segment_name,s.blocksfrom dba_segments s where s.owner='HR' and  s.segment_name='T04209_UNAME';



SEGMENT_NAME

BLOCKS

1

T04209_UNAME

256该表共有256个块




实验第4步:

查看实验用表t04209_unameobject_id

在任一个实例上执行:

selecto.object_name,  o.object_idfrom dba_objects o  where o.owner='HR' and o.object_name='T04209_UNAME';



OBJECT_NAME

OBJECT_ID

1

T04209_UNAME

52533




实验第5步:

在任一个实例上执行:

select* from  x$OBJECT_AFFINITY_STATISTICS where object=52533;

无输出




实验第6步:

在实例1update这个表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;



ADDR

INDX

INST_ID

OBJECT

NODE

OPENS

1

B7F4C4C8

7

2

↖INST_ID为2代表Mater实例为实例2

52533

1 ↖NODE1代表Owner实例为实例1

245←BL    Open次数,这个表共有256个块几乎占全部。说明update大部分的行。



实验第8步:

等实例1update结束后,再查x$OBJECT_AFFINITY_STATISTICS没有输出

重新在实例1上做 updatex$OBJECT_AFFINITY_STATISTICS仍然没有输出。

这就说明:如果缓存中的数据块已经处于适当的模式,就没有必要在数据块上再打开BL锁。所以如果会话反复存取同一个数据块而不请求额外的BL锁,那么在x$OBJECT_AFFINITY_STATISTICSBL锁的Open计数就不会增加。



实验第9步:

在实例1Alter  system flush buffer_cache后,再做同样的update



实验第10步:

在任一个实例上执行:

select* from  x$OBJECT_AFFINITY_STATISTICS where object=52533;



ADDR

INDX

INST_ID

OBJECT

NODE

OPENS1

1

B688A1D0

7

2↖INST_ID为2代表Mater实例为实例2

52533

1↖NODE1代表Owner实例为实例1

248←BL    Open次数


实验第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;



ADDR

INDX

INST_ID

OBJECT

NODE

OPENS

1

B688A1D0

20

2

52533

1

2562由于两个实例同时更改,产生了大量BL    Open

2

B688A1D0

21

2

52533

2

1053↙BL Open的增加是为了进行gc    cr




实验第13步:

在任一个实例上执行:

selectto_char(52533,'xxxxxxxx')from dual;



TO_CHAR(52533,'XXXXXXXX')

1

cd35




实验第14步:

在任一个实例上执行: 查看两个实例此时的资源状态

selectinst_id ,resource_name,on_convert_q, on_grant_q,master_node, next_cvt_levelfrom  gv$ges_resourcewhereresource_name like'[0xcd35]%';



INST_ID

RESOURCE_NAME

ON_CONVERT_Q

ON_GRANT_Q

MASTER_NODE

NEXT_CVT_LEVEL

1

2

[0xcd35][0x0],[TM]

0

1

0

KJUSERNL

2

2

[0xcd35][0xc09281d],[IV]

0

1

0

KJUSERNL

3

1

[0xcd35][0x0],[TM]

0

1

0

KJUSERNL

4

1

[0xcd35][0xc09281d],[IV]

0

1

0

KJUSERNL



实验第15步:

在任一个实例上执行: 查看两个实例此时的锁状态

selectinst_id,grant_level,request_level,resource_name2,PID , TRANSACTION_ID0, TRANSACTION_ID1 from gv$ges_enqueue  whereresource_name2 like '52533%';



INST_ID

GRANT_LEVEL

REQUEST_LEVEL

RESOURCE_NAME2

PID

TRANSACTION_ID0

TRANSACTION_ID1

1

1

KJUSERCW

KJUSERCW

52533,0,TM

27730

1835009

33

2

1

KJUSERCW

KJUSERCW

52533,0,TM

0

0

0

3

1

KJUSERPR

KJUSERPR

52533,201926685,IV    

7283

0

0

4

1

KJUSERPR

KJUSERPR

52533,201926685,IV    

0

0

0

5

2

KJUSERCW

KJUSERCW

52533,0,TM

9231

1703938

111

6

2

KJUSERPR

KJUSERPR

52533,201926685,IV

7200

0

0

KJUSERPRBL锁,KJUSERCW是表级共享锁,KJUSERTM是行级独占锁,KJUSERNL是空锁,KJUSEREXlock table之类命令产生的独占锁

3Buffer Cache中的数据块变化前必需询问GRD的完整实验过程


未完,下一部分