首页 / 数据库 / MySQL / 初探Cache Fusion对block的锁管理
本文以双节点RAC为例,揭示了在执行select及DML操作过程中,Cache Fusion在幕后是如何对block进行锁管理的。 ###实例1上查询scott.t0820_1表 select * from scott.t0820_1; ID ---------- 2 select dbms_rowid.rowid_relative_fno(rowid) rfno,dbms_rowid.rowid_block_number(rowid) blkno from scott.t0820_1; RFNO BLKNO ---------- ---------- 6 255 select to_char(6,"xxx"),to_char(255,"xxx") from dual; TO_C TO_C ---- ---- 6 ff select object_id,data_object_id from dba_objects where object_name="T0820_1"; OBJECT_ID DATA_OBJECT_ID ---------- -------------- 15976 15976###用到的脚本 //// get_buffer_stat.sql //// col object_name for a10 select (select object_name from dba_objects where object_id = b.obj) as object_name,decode (state,0, "Free", 1, "XCUR", 2, "SCUR", 3, "CR", 4,"BEING READ",5, "MREC", 6, "IREC", 7, "WRITE_CLONE", 8, "PI") state,mode_held, le_addr, cr_scn_bas, cr_scn_wrp from x$bh b where obj = 15976 and dbablk = 255 and class = 1; //// get_resource_name.sql,获得block在GRD内存的资源名//// col hexname for a35 col resource_name for a15 set linesize 170 select b.kjblname hexname, b.kjblname2 resource_name,b.kjblgrant, b.kjblrole, b.kjblowner,b.kjblmaster,b.KJBLPKEY,b.kjblsid,b.kjblrequest from x$le a, x$kjbl b where a.le_kjbl=b.kjbllockp and a.le_addr = ( select le_addr from x$bh where dbablk = 255 and obj = 15976 and class = 1 and state <> 3); //// get_resource_stat.sql 获得block资源的授权访问信息,注意:因为v$dlm_ress只在block的主节点上才能查到,而v$ges_enqueue在两节点上都有,所以查询结果表示block以inst_id所指节点为主节点,inst_id=mast+1 //// col resource_name for a34 col state for a8 col mast for 9999 col grnt for 9999 col cnvt for 9999 set linesize 160 select a.inst_id,a.resource_name, b.state, a.master_node mast, a.on_convert_q cnvt,a.on_grant_q grnt, b.request_level, b.grant_level, b.owner_node from gv$dlm_ress a, gv$ges_enqueue b where upper(a.resource_name) = upper(b.resource_name1) and a.resource_name like "%[0xff][0x6]%" and a.inst_id=b.inst_id; //// get_master_node.sql 获得block资源的主节点 //// col resource_name for a34 col state for a8 col mast for 9999 col grnt for 9999 col cnvt for 9999 set linesize 160 select inst_id,resource_name,ON_GRANT_Q,ON_CONVERT_Q,master_node from gv$ges_resource where resource_name like "[0xff][0x6],[BL]%"; //// get_ges_enqueue.sql 查询gv$ges_enqueue视图//// set linesize 180 select inst_id,owner_node,resource_name1,resource_name2,GRANT_LEVEL,REQUEST_LEVEL from gv$ges_enqueue where upper(RESOURCE_NAME1) like "[0XFF][0X6],[BL]%" order by inst_id,owner_node;---实例1上执行get系列脚本 ###因为之前实例1访问过这个block所以以下脚本有输出 SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- -------- ---------- ---------------- ---------- ---------- T0820_1 SCUR 0 000000008BFAEE38 0 0###block的主节点在实例2上,因为MASTER_NODE=1 SQL> @get_master_node.sql INST_ID RESOURCE_NAME ON_GRANT_Q ON_CONVERT_Q MASTER_NODE ---------- ---------------------------------- ---------- ------------ ----------- 2 [0xff][0x6],[BL][ext 0x0,0x0] 1 0 1 ###获得block的资源名称,只有访问过这个block的节点才能查到 SQL> @get_resource_name HEXNAME RESOURCE_NAME KJBLGRANT KJBLROLE KJBLOWNER KJBLMASTER KJBLPKEY KJBLSID KJBLREQUE ----------------------------------- --------------- --------- ---------- ---------- ---------- ---------- ---------- --------- [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERPR 0 0 1 15976 0 KJUSERNL <---KJBLPKEY等于object_id ###inst_id=1或者MAST=0都表示block的主节点在实例2上,owner_node=0、state=GRANTED及grant_level=KJUSERPR表示目前实例1读取了这个block,经实例2的允许实例1在block上加了KJUSERPR锁 SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 2 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 1 0 1 KJUSERNL KJUSERPR 0 ************* * 人工将主节点从实例2调整为实例1 ************* ---在实例1上执行 ###尝试将block的主节点从实例2人工调整为实例1 SQL> oradebug setmypid; Statement processed. SQL> oradebug lkdebug -m pkey 15976 Statement processed.###MASTER_NODE=0表明block 6/255的主节点为实例1 SQL> @get_master_node.sql INST_ID RESOURCE_NAME ON_GRANT_Q ON_CONVERT_Q MASTER_NODE ---------- ---------------------------------- ---------- ------------ ----------- 1 [0xff][0x6],[BL][ext 0x0,0x0] 1 0 0###以下两个输出除了MASTER变成0,其他均和上一轮的输出保持一致 SQL> @get_resource_name HEXNAME RESOURCE_NAME KJBLGRANT KJBLROLE KJBLOWNER KJBLMASTER KJBLPKEY KJBLSID KJBLREQUE ----------------------------------- --------------- --------- ---------- ---------- ---------- ---------- ---------- --------- [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERPR 0 0 0 15976 0 KJUSERNL SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSERPR 0 <---inst_id说明主节点是实例1,owner_node=0表示实例1曾经访问过block 6/255、并持有KJUSERPR锁---实例2上执行get系列脚本 ###实例2尚未访问过该block所以无输出 SQL> @get_buffer_stat.sql no rows selected###来看看实例2上此时的输出 SQL> @get_master_node.sql INST_ID RESOURCE_NAME ON_GRANT_Q ON_CONVERT_Q MASTER_NODE ---------- ---------------------------------- ---------- ------------ ----------- 1 [0xff][0x6],[BL][ext 0x0,0x0] 1 0 0 <---ON_GRANT_Q=1表示block已经被授予某个节点的访问权限 SQL> @get_resource_name no rows selected <---因为实例2尚未访问过该block所以尚无输出 SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSERPR 0---实例2上执行block 6/255的查询、然后执行get系列脚本 select * from scott.t0820_1; ID ---------- 2 SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- -------- ---------- ---------------- ---------- ---------- T0820_1 SCUR 0 000000008AFF8E28 0 0 SQL> @get_master_node.sql INST_ID RESOURCE_NAME ON_GRANT_Q ON_CONVERT_Q MASTER_NODE ---------- ---------------------------------- ---------- ------------ ----------- 1 [0xff][0x6],[BL][ext 0x0,0x0] 1 0 0 SQL> @get_resource_name HEXNAME RESOURCE_NAME KJBLGRANT KJBLROLE KJBLOWNER KJBLMASTER KJBLPKEY KJBLSID KJBLREQUE ----------------------------------- --------------- --------- ---------- ---------- ---------- ---------- ---------- --------- [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERPR 0 1 0 15976 0 KJUSERNL <---KJBLOWNER=1###inst_id=1表示block主节点是实例1,输出的两行分别表示实例1、实例2均以读的方式访问block SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSERPR 0 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSERPR 1###作为主节点的实例1记录了所有访问过该block的节点信息(inst_id=1的两条记录,owner_node=0记录实例1的访问,owner_node=1记录了实例2的访问),主节点所掌握的信息在RAC里称为master metadata;而实例2只记录了自己对于block的访问(inst_id=2的那条记录),非主节点掌握的信息在RAC里被称为shadow metadata SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 1 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERPR KJUSERNL 1 1 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERPR KJUSERNL 2 1 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERPR KJUSERNL ************* * 人工将主节点从实例1调整为实例2,先后在实例2、实例1上发起update操作 ************* ---实例2上通过执行oradebug把block 6/255的主节点再次重置为实例2,观察master metadata是否都转移到了实例2上 SQL> oradebug setmypid; Statement processed. SQL> oradebug lkdebug -m pkey 15976 Statement processed.###果然实例2对该block的记录变为了两条(inst_id=2有两条),表明主节点对应的实例上保存RAC环境里所有节点对某个block访问时持有的锁信息 SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 1 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERPR KJUSERNL 2 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERPR KJUSERNL 2 1 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERPR KJUSERNL---实例2上发起对block的更改,运行get系列脚本 SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- ----------- ---------- ---------------- ---------- ---------- T0820_1 SCUR 0 0000000089F90298 0 0 update scott.t0820_1 set id=id+1; commit; SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- -------- ---------- ---------------- ---------- ---------- T0820_1 XCUR 0 0000000089F90298 0 0 T0820_1 CR 0 00 1947380 0 SQL> @get_resource_name.sql HEXNAME RESOURCE_NAME KJBLGRANT KJBLROLE KJBLOWNER KJBLMASTER KJBLPKEY KJBLSID KJBLREQUE ----------------------------------- --------------- --------- ---------- ---------- ---------- ---------- ---------- --------- [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX 0 1 1 15976 0 KJUSERNL SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 2 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 1 0 1 KJUSERNL KJUSEREX 1 SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 2 1 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL <---作为主节点的实例2 master metadata信息里包含了自己(OWNER_NODE=1)修改block时加的X锁---实例1上运行get系列脚本 SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- ----------- ---------- ---------------- ---------- ---------- T0820_1 CR 0 00 1947379 0 SQL> @get_resource_name.sql no rows selected <---get_resource_name.sql脚本实质上是关联x$le、x$kjbl两张表得出实例对block施加的锁信息,没有输出是因为实例1的cache里只有CR类型的buffer,只能给自己使用,不能分享其它节点,所以无需持有任何锁 SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 2 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 1 0 1 KJUSERNL KJUSEREX 1 <---只剩下了实例2 update block时留下的X锁,实例1先期加在block上的Share锁已解除 SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 2 1 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL <---作为主节点的实例2 master metadata信息里包含了自己(OWNER_NODE=1)修改block时加的X锁,没有inst_id=1的记录说明实例1没有关于该block的shadow metadata---实例1上发起对block的更改,运行get系列脚本 SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- -------- ---------- ---------------- ---------- ---------- T0820_1 CR 0 00 1947379 0 update scott.t0820_1 set id=id+1; commit; SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- -------- ---------- ---------------- ---------- ---------- T0820_1 XCUR 0 0000000089F95818 0 0 T0820_1 CR 0 00 0 T0820_1 CR 0 00 1947379 0 SQL> @get_resource_name.sql HEXNAME RESOURCE_NAME KJBLGRANT KJBLROLE KJBLOWNER KJBLMASTER KJBLPKEY KJBLSID KJBLREQUE ----------------------------------- --------------- --------- ---------- ---------- ---------- ---------- ---------- --------- [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX 0 0 1 15976 0 KJUSERNL <---KJBLOWNER=0表示实例1访问了block SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 2 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 1 0 1 KJUSERNL KJUSEREX 0 <---OWNER_NODE=0说明之前实例2修改block时加上的X锁已被释放,改为记录本次实例1修改block时的锁信息 SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 1 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL <---因为最近一次修改block是从实例1发起,所以实例1的GRD里记录了OWNER_NODE=0(代表实例1)修改时持有的锁信息;实例2之所以会记录实例1锁修改时的锁信息是因为实例2是master node,必须记录所有访问过该block的节点信息 2 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL---实例2上运行get系列脚本 SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- -------- ---------- ---------------- ---------- ---------- T0820_1 CR 0 00 1950280 0 T0820_1 CR 0 00 1947380 0 SQL> @get_resource_name.sql <---实例1发起更改后,实例2 cache里只有CR类型的buffer,只能给自己使用,不能分享其它节点,所以无需持有任何锁(实质是x$bh.le_addr=NULL所以与x$le.le_addr关联无结果) no rows selected SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 2 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 1 0 1 KJUSERNL KJUSEREX 0 SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 1 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL 2 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL如果此时我们将block的master node再一次指回实例1,那么get_ges_enqueue.sql仅会显示inst_id=1的记录,因为实例2不再是主节点,所以没必要保存其它节点的block锁信息 ************* * 人工将主节点从实例2调整回实例1,观察v$ges_enqueue视图的内容 ************* ---实例1执行 SQL> oradebug setmypid; Statement processed. SQL> oradebug lkdebug -m pkey 15976 Statement processed. SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 1 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL <---仅有节点1(owner_node=0)的block锁信息保存在节点1(inst_id=1)上************* * 重启数据库后,执行update但不提交的情况下,block锁资源查询 ************* ###重启数据库 srvctl stop database -d SUSEdb1 -o immediate srvctl start database -d susedb1---实例1、实例2分别查询scott.t0820_1表 select * from scott.t0820_1;---实例1查询block 6/255资源上的锁持有情况,此时实例1是主节点,实例1、实例2都对block持有共享读模式的锁 SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSERPR 0 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSERPR 1---实例1执行update后不提交,紧接着运行get系列脚本 update scott.t0820_1 set id=id+1; SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- -------- ---------- ---------------- ---------- ---------- T0820_1 XCUR 0 0000000089F90C18 0 0 T0820_1 CR 0 00 2015904 0 SQL> @get_resource_name.sql HEXNAME RESOURCE_NAME KJBLGRANT KJBLROLE KJBLOWNER KJBLMASTER KJBLPKEY KJBLSID KJBLREQUE ----------------------------------- --------------- --------- ---------- ---------- ---------- ---------- ---------- --------- [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX 0 0 0 15976 0 KJUSERNL SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSEREX 0 SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 1 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL---实例2运行get系列脚本查看资源状态信息 SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- ----------- ---------- ---------------- ---------- ---------- T0820_1 CR 0 00 2015903 0 SQL> @get_resource_name.sql no rows selected <---原有的KJUSERPR锁解除 SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSEREX 0 SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 1 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL ---实例2 update同一条记录, update scott.t0820_1 set id=id+1; <----处于等待 SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- ----------- ---------- ---------------- ---------- ---------- T0820_1 XCUR 0 0000000089F96198 0 0 T0820_1 CR 1 00 2016429 0 T0820_1 CR 0 00 2015903 0 SQL> @get_resource_name.sql HEXNAME RESOURCE_NAME KJBLGRANT KJBLROLE KJBLOWNER KJBLMASTER KJBLPKEY KJBLSID KJBLREQUE ----------------------------------- --------------- --------- ---------- ---------- ---------- ---------- ---------- --------- [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX 64 1 0 15976 0 KJUSERNL SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSERNL 0 <---如果实例1的buffer cache里有pi类型的block在,才会有这条记录 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSEREX 1 <---虽然实例2的会话因为锁而处于等待,但GRANT_LEVEL还是显示为KJUSEREX,从GC视图看不出行级锁的存在 SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 1 0 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSERNL KJUSERNL 1 1 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL 2 1 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL <---虽然实例2的会话因为锁而处于等待,但GRANT_LEVEL还是显示为KJUSEREX---实例1执行get系列脚本 SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- -------- ---------- ---------------- ---------- ---------- T0820_1 CR 0 00 2016429 0 T0820_1 CR 0 00 2016430 0 T0820_1 CR 0 00 2015904 0 SQL> @get_resource_name.sql no rows selected <---因为实例1的buffer里都是CR类型的block,所以这里必然返回0条记录 SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSEREX 1 <---实例1的buffer cache里有pi类型的block因为checkpoint发生变成CR了,所以相比上次在实例2的执行,结果里少了OWNER_NODE=1的记录 SQL> @get_ges_enqueue.sql INST_ID OWNER_NODE RESOURCE_NAME1 RESOURCE_NAME2 GRANT_LEV REQUEST_L ---------- ---------- ------------------------------ ------------------------------ --------- --------- 1 1 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL <---相比上次在实例2执行的输出少了owner_node=0、GRANT_LEVEL/REQUEST_LEVEL=KJUSERNL的记录,因为实例1上的pi block变成了CR 2 1 [0xff][0x6],[BL][ext 0x0,0x0] 255,6,BL KJUSEREX KJUSERNL************* * 实例1回滚后,block资源使用情况观察 ************* ---实例1 执行rollback回滚刚才的更改 SQL> update scott.t0820_1 set id=id+1; 1 row updated. SQL> rollback; Rollback complete. SQL> @get_buffer_stat.sql <---回滚后可看到PI出现 OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- ----------- ---------- ---------------- ---------- ---------- T0820_1 PI 0 0000000089F90C18 0 0 T0820_1 CR 0 00 2016429 0 T0820_1 CR 0 00 2016430 0 T0820_1 CR 0 00 2015904 0 SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSERNL 0 <---PI block保留有KJUSERNL锁,这点与CR block不同,后者没有任何锁 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSEREX 1 SQL> alter system checkpoint; System altered. SQL> @get_buffer_stat.sql OBJECT_NAM STATE MODE_HELD LE_ADDR CR_SCN_BAS CR_SCN_WRP ---------- -------- ---------- ---------------- ---------- ---------- T0820_1 CR 0 00 2020380 0 <---checkpoint发生后PI变成了CR T0820_1 CR 0 00 2016429 0 T0820_1 CR 0 00 2016430 0 T0820_1 CR 0 00 2015904 0 SQL> @get_resource_stat.sql INST_ID RESOURCE_NAME STATE MAST CNVT GRNT REQUEST_L GRANT_LEV OWNER_NODE ---------- ---------------------------------- -------- ----- ----- ----- --------- --------- ---------- 1 [0xff][0x6],[BL][ext 0x0,0x0] GRANTED 0 0 1 KJUSERNL KJUSEREX 1 <---REQUEST_LEVEL/GRANT_LEVEL均为KJUSERNL的记录消失总结: 1、当多个节点在自己的buffer cache里拥有同一block的scur buffer时,他们对block持有的KJUSERPR类型的锁信息可以并存于GRD 2、当某节点修改了block,该block xcur类型的buffer出现在执行修改操作节点的buffer cache,其余节点只会有PI或CR类型的buffer,对于每个PI类的buffer GRD里各保留一行REQUEST_LEVEL/GRANT_LEVEL均为KJUSERNL的记录;对于CR类的buffer GRD里不保留任何锁信息 本文永久更新链接地址
收藏该网址