阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

初探Cache Fusion对block的锁管理

112次阅读
没有评论

共计 21315 个字符,预计需要花费 54 分钟才能阅读完成。

本文以双节点 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 里不保留任何锁信息

本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-09/135135.htm

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-22发表,共计21315字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中