先看一张图:
上图中,左半部分代表一个数据块(Da
我们知道数据块是Oracle最基本的存储单元,而每个数据块都分成两部分:数据块头和数据块体(上图中,左上部分代表数据块头,左下部分代表数据块体)。数据块头存放的是一些管理信息,和事务有关的部分就是ITL(Interested Transaction List)。数据块体存放的就是一条条用户记录。注意,数据块体里面的每一条记录也都是分成两部分:记录头和记录体,记录头中是描述信息,比如列宽度,和事务有关的是ITL Entry Pointer字段。
整个行级锁共涉及以下4种数据结构:
§ ITL:每个数据块的块头部分有一个叫ITL的数据结构,用于记录哪些事务修改了这个数据块的内容。可以把它想象成一个表格,每个表项对应一个事务,包括事务号、事务是否提交等重要信息。
§ 记录头ITL索引:每条记录的记录头部有一个字段,用于记录ITL表项号,可以看作是指向ITL表的指针。
§ TX锁:这个锁代表一个事务,属于lock控制机制,有Resource Structure、Lock Structure、Enqueue算法。
§ TM锁:这个锁也属于lock控制机制,用于保护对象(表、视图等)的定义不被修改。
好,有了上面的简要认识后,下面介绍下行级锁机制:
当一个事务开始时,必须先申请一个TX锁,这种锁保护的资源是回滚段、回滚数据块。因此这个申请也就意味着:用户进程必须先申请到回滚段资源后才能开始一个事务,才能执行DML语句修改数据。
申请到回滚段资源以后,用户事务就可以修改数据了。在修改数据表的记录时,需要遵循下面的操作顺序。
(1)首先要获得这个表的TM锁,这个锁用于保护事务执行过程中其他用户不能修改表结构。
(2)事务修改某个数据块中的记录时,首先要先在该数据块块头的ITL表中申请一个空闲表项,并在其中记录事务号,实际就是记录这个事务要使用的回滚段地址。
(3)事务修改该数据块中的某条记录时,会设置该记录头部的ITL索引指向上一步申请到的表项。然后再修改记录内容,修改前先在回滚段对记录修改前的状态做一个拷贝,然后才能修改数据记录,这个拷贝用于以后的回滚、恢复或一致性读。
(4)当其他用户并发修改这条记录时,会根据记录头的ITL索引读取ITL表项内容,查看这个事务是否已经提交。
(5)如果没有提交,则这个用户的TX锁会等待前一个用户的TX锁的释放。
从上面行级锁的工作机制可看出,无论一个事务修改多少个表的多少条记录,该事务真正需要的只是一个TX锁、每个表一个的TM锁,内存开销非常小。而所谓的“行级锁”其实只是数据块头、数据记录头的一些字段,不会消耗额外资源。
因此,对于Oracle的“行级锁”必须要正确的理解,它不是Oracle中通常意义上的锁,而只是一个所谓的“锁”,虽然有锁的功能,但是没有锁的开销。
可从下面一个实验中验证这一点:当用户被阻塞时,不是被某条记录阻塞,而是被TX锁阻塞,可以用下面的例子验证。
会话1:
[oracle@ligle-db ~]$ sqlplus ligle/ligle;
SQL*Plus: Release 10.2.0.4.0 - Production on Fri Dec 10 19:39:31 2010
Copyright (c) 1982, 2007, Oracle. All Rights Reserved.
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Production
With the Partitioning, OLAP, Da
SQL> select * from test;
ID NAME
---------- --------------------
1 libai
2 zhuzhiwu
3 caogui
SQL> savepoint a;
Savepoint created.
SQL> update test set name='qinshihuang' where id=2;
1 row updated.
SQL> select userenv('sid') from dual;
USERENV('SID')
--------------
159
会话2:
SQL> update test set name='dumu' where id=2;
此时,我们会发现会话2锁执行的update语句被挂起,这是因为会话2是被会话1的TX锁阻塞的,而不是被会话1的行级锁阻塞。
在会话3中查看锁的情况:
SQL> select username,event,sid,blocking_session from v$session where username='LIGLE'
USERNAME EVENT SID BLOCKING_SESSION
------------------------------ ------------------------------ ---------- ----------------
LIGLE enq: TX - row lock contention 144 159
LIGLE SQL*Net message from client 151
LIGLE SQL*Net message from client 159
从第一行,可以看到会话144被会话159的TX锁阻塞。
以上内容参考《大话Oracle RAC 集群 高可用性 备份与恢复》