ORACLE的行锁的实现、一致性读、多版本并发控制MVCC

    在执行包含update,delete,merge,select for update语句时事务必须得到一个TX锁,也就是事务锁。而且会持有这个所直到提交或回滚。事务未提交时,为了实现事务一致性,其它事务不能访问正在修改的数据,其它事务必须排队等待这个事务提交或回滚。下面先介绍排队的实现。
    当事务锁住某些数据时,其它事务必须排队等待,ORACLE在SGA中分配的Reource Structure、Lock Structure数据结构通过Enqueue算法实现。
bb

     Resource Structure有三个与并发控制有关的成员:Owner,Waiter,Converter。它们其实是指向Lock Structure链表的指针。分别表示已经获取资料访问权的进程、等待进程、锁转换等待进程。资源由ID1,ID2标识,在上图中 TM表示资源类型,256表示dba_objects中的objectid

    Lock Structure用于记录访问共享资源的数据结构,它记录了锁模式、进程ID等信息 。当一个进程访问共享资源时,必须要去锁定该资源,所以它先要从内存中申请一个Lock Structure, 然后根据封锁是否成功 加入不同的链表。

    行级锁事务会定位到修改的数据在哪个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链表优先,将锁分配给最先进入队列的请求者。

  下面修改表中的数据,不提交,然后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
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没有。
    一致性读,MVCC
     事务开始修改数据前,必须在回滚段中获得一个事务槽、分配空间,创建数据的前镜像,然后事务信息同样被记录在块头的ITL上,此后事务修改才能继续进行。ORACLE以此来保证事务是可以回退的。当事务提交时,会在回滚段中将该事务标记为已经提交。 这时就创建了数据在提交时对应的SCN的“版本”,当有多个事务成功修改了同一数据时,在回滚段中就有了行在不同SCN的版本。
   当一个读操作读到某一个数据块上,发现该数据块有被修改过的痕迹(锁标志指向ITL),会需要通过SCN来检查是读取当前数据还是修改前的数据,以保证读取到的所有数据都是事务开始时的数据来保证数据的一致性。如果数据块上事务的SCN<读操作开始时的SCN,说明数据块上的更改是发生在读操作之前的,就可以直接读取数据块上的数据否则就需要从回滚段中读取事务开始时的数据 。 这就是语句级的一致性读,如果要得到事务级的读一致性防止不可重复读和幻像读的话必须使用Serialable或Read Only 隔离级别 。它们的区别是Read Only 从 回滚段中获取数据不阻塞其它事务,而Searialable 级别下其它事务将被阻塞不排队直接得到ORA-01877 can't serialize access for this transaction
  在其它单版本+锁实现的数据库如SQL SERVER2000、DB2隔离级别在Read Commit,事务在修改数据时,查询必须等待事务完成,所以查询得到的都是最新的数据。在一个长的统计查询中, 如果有其它事务修改查询涉及的行并提交 ,查询可能得到错误的答案。必须在行上加共享读锁才能得到 正确的答案。加了共享读锁就阻塞了写入。而ORACLE 通过从回滚段中读取 查询开始时一致的结果,避免阻塞写入。
    总之在ORACLE中写不会阻塞读,读不会阻塞写。这就大大提高了系统的并发性。这就MVCC多版本并发控制优点。
参考资料 
关于事务对数据块的操作过程的分析和试验

Internal Implementation of Oracle Locks (Enqueue)


Thomas Kyte 大作ORACLE 编程艺术第7章 并发与多版本
附录:ITL 字段详细说明

Xid :Transaction ID,xid = Undo Segment Number + Transaction Table Slot Number + Wrap即回滚段号+事务槽编号+序号(同一个事务可能具有多个 SCN ,实际上每一个 DML 操作都有一个SCN)

UbaUndo Block Address。回滚数据块地址,该地址对应了该事务在回  滚段中记录的回滚数据的地址。分解该地址:0x0080051a.059f.34第一段是回滚数据块的地址,包括回滚段文件号和数据块号;第二段是回滚序列号;第三段是回滚记录号。

Lck:事务锁定的行数。

Flag事务标志位。这个标志位就记录了这个事务的操作

---- = transaction is active, or committed pending cleanout
C--- = transaction has been committed and locks cleaned out
-B-- = this undo record contains the undo for this ITL entry
--U- = transaction committed (maybe long ago); SCN is an upper bound
---T = transaction was still active at block cleanout SCN

Scn/Fsc:快速提交(Fast Commit Fsc)的SCN或者Commit SCN

另推荐CSDN落落专栏的文章:ORACLE是如何根据ITL找到前镜像的

fj.png0905E_2.gif

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/9683969/viewspace-672920/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/9683969/viewspace-672920/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值