|
在执行包含update,delete,merge,select for update语句时事务必须得到一个TX锁,也就是事务锁。而且会持有这个所直到提交或回滚。事务未提交时,为了实现事务一致性,其它事务不能访问正在修改的数据,其它事务必须排队等待这个事务提交或回滚。下面先介绍排队的实现。
当事务锁住某些数据时,其它事务必须排队等待,ORACLE在SGA中分配的Reource Structure、Lock Structure数据结构通过Enqueue算法实现。
Resource Structure有三个与并发控制有关的成员:Owner,Waiter,Converter。它们其实是指向Lock Structure链表的指针。分别表示已经获取资料访问权的进程、等待进程、锁转换等待进程。资源由ID1,ID2标识,在上图中 TM表示资源类型,256表示dba_objects中的objectid
Lock Structure用于记录访问共享资源的数据结构,它记录了锁模式、进程ID等信息 。当一个进程访问共享资源时,必须要去锁定该资源,所以它先要从内存中申请一个Lock Structure, 然后根据封锁是否成功 加入不同的链表。
[size=11.6667px] 行级锁:事务会定位到修改的数据在哪个block,哪个行,然后检查行的LB(Lock Byte锁标识位)
tl: 73 fb: --H-FL-- lb: 0x1 cc: 12 (lb=0x1说明编号0x1的ITL代表的事务锁住本行)
锁标识位指向的ITL(即Interested Transaction List,直译过来就是感兴趣的事物列表。也 就是对该数据块产生影响的事务。它包含了事务的信息:ITL序号、事务编号、回滚段地址、事务标识、锁定的行数、SCN/FSC。)
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0006.011.000004c7 0x00c001fd.0105.13 ---- 1 fsc 0x0000.00000000
检查ITL对应的事务的状态是不是active ,如果是则把这个Lock Structure挂到Resource Structure的Waiter链表中 。
如果LB指向ITL对应的事务状态不是active,则将Lock Structure挂到Resource Structure的Owner链表,然后在回滚段中获得一个事务槽、分配空间,创建数据的前镜像,然后到块头申请ITL表项,并修改block的itl Lck标志位,修改记录lb标志位指向事务所在的itl。事务结束时,将会去检查Waiter和Conversion 链表,Conversion链表优先,将锁分配给最先进入队列的请求者。
[size=11.6667px]下面修改表中的数据,不提交,然后DUMP数据块,查看DUMP中的ITL和行的LB
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
/*设置跟踪文件的标识符,方便查找*/
SQL> alter session set tracefile_identifier='dump';
Session altered.
/*查找行的文件号和块号*/
SQL> select dbms_rowid.rowid_relative_fno(rowid)file_id,dbms_rowid.rowid_block_number(rowid)block_id from department where depid='D2285';
FILE_ID BLOCK_ID
---------- ----------
7 1259[size=11.6667px]
SQL> update department set note='lock test' where depid='D2285';
1 row updated
SQL> alter system dump datafile 7 block 1259;
System altered./*下面是DUMP内容,省略部分内容*/
Block header dump: 0x01c004eb
Object id on Block? Y
seg/obj: 0x12333 csc: 0x00.f765d1 itc: 2 flg: E typ: 1 - DATA
brn: 0 bdba: 0x1c004e8 ver: 0x01 opc: 0
inc: 0 exflg: 0
Itl Xid Uba Flag Lck Scn/Fsc
0x01 0x0006.011.000004c7 0x00c001fd.0105.13 ---- 1 fsc 0x0000.00000000
0x02 0x0005.016.000003f5 0x00c006a0.0131.2a C--- 0 scn 0x0000.00f76566
bdba: 0x01c004eb
data_block_dump,data header at 0xfffffd7ffcb84a64
block_row_dump:
tab 0, row 0, @0x2e7
tl: 73 fb: --H-FL-- lb: 0x1 cc: 12
col 0: [ 5] 44 32 32 38 35
col 1: [ 6] cf af bd a8 b3 c9
col 2: *NULL*
col 3: [ 6] d2 b5 ce f1 b2 bf
col 4: [ 5] 30 30 31 32 33
col 5: [ 9] 64 75 6d 70 20 74 65 73 74
col 6: [11] 78 6d 09 1c 10 34 0d 08 76 bf 80
col 7: [ 5] 46 61 6c 73 65
col 8: [ 4] 58 41 30 31
col 9: [ 5] 44 32 32 38 35
col 10: *NULL*
col 11: [ 2] 30 31
tab 0, row 1, @0x378
tl: 64 fb: --H-FL-- lb: 0x0 cc: 12
col 0: [ 5] 44 32 32 38 36
col 1: [ 6] cf af bd a8 b3 c9
col 2: *NULL*
到这里我们可以发现,ORACLE的行级锁 只是数据块头的ITL、数据行头的LB锁标识位,不需要消耗额外的资源。 需要注意的是事务并不是被行阻塞,而是被其它的事务阻塞 , 会话通过上述的字段检测是否被阻塞达到锁的效果。 Lock Structure 可以理解为一个session,如果第一行的 封锁都用resource 、lock两种数据结构那内存需求和维护开销将是一个噩梦。 所以某些数据库就有锁升级机制,而ORACLE没有。
|
|