行级锁

行级锁(Row-level Lock)

先看一张图:

行级锁 - m15142436758 - 简单的博客


上图中,左半部分代表一个数据块(Data Block),而后半部分代表的是回滚段(Undo Segment)。

我们知道数据块是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, Data Mining and Real Application Testing options
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 集群 高可用性 备份与恢复》

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值