Lock概述

Lock,首先代表一种控制机制,其次在这个机制中有一个成员也叫LOCK

LOCK框架包含三个组件:Resource Structure(资源)、Lock Structure(锁)和Enqueue(排队机制)。Resource和Lock是数据结构,Enqueue是算法。

Resource Structure

Oracle对于每个需要进行“并发控制”的资源,都在SGA中用一个数据结构来描述它,这个数据结构叫做Resource Structure。这个数据结构中的有3个与“并发控制”相关的成员:Onwer,Waiter,Converter。这是3个指针,分别指向3个有 Lock Structure组成的链表。

Lock Structure

每个进程要访问共享资源时,必须要先“锁定”该资源。这个动作实际就是从内存中申请一个Lock Structure,在其中记录”锁模式,进程ID“等重要信息。然后看是否能立即获得访问权,如果不能,则把Lock Structure挂到Resource Structure的Waiter链表中。如果能获得访问权,则把 Lock Structure挂到Resource Structure的 Onwer 链表中。上面说到的 Resource Structure中的 Onwer,Waiter,Converter三个成员就是指向由Lock Structure组成的链表的指针。Lock的组成示意图如下

         


锁模式

Oracle中的对象可以分为两类,简单对象(Simple Objects)和复合对象(Compound Objects)。数据表是典型的复合对象,表中的每条记录是典型的简单对象。对于简单对象只有3中模式:Null、Share、Exclusive。而 对于复合对象,除了 Null、Share、Exclusive这3中模式外,还有 Sub-Shared、Sub-Exclusive、Shared-sub-exclusive 。

Null:模式值为1。不妨碍任何访问,这种模式主要用于数据字典。

Share: 模式值为4。 拥有者本身对资源进行只读访问,允许其他用户并发只读访问。
Exclusive: 模式值为6。 拥有者本身对资源进行修改访问,不允许其他用户任何并发访问
Sub-Shared: 模式值为2。 当需要以Share模式访问复合对象中的成员时,需要对复合对象加这种模式的锁,而对成员加Shared模式的锁;这种模式有不同的缩写(SS,RS,CR)。
Sub-Exclusive: 模式值为3。 当要以Exclusive模式访问复合对象中的成员时,需要对复合模式对象加这种模式的锁,而对成员加Exclusive模式的锁;这种锁模式有不同的缩写(SX,RX,CR)。
Shared-sub-exlusive: 模式值为5。 当需要对复合对象的成员进行Exclusive模式访问,同时要对复合对象本身进行Shared模式访问时,需要对复合对象加这种模式的锁;这种模式的锁有不同的缩写(SSX,SRX,PW)
为什么要引入意向锁
之所以把对象划分成为两种类型,也是从性能角度考虑。

如果在一个数据库管理系统中,同时支持多种锁定粒度供事务选择,这种锁定方法就被称为多粒度锁定(multiple granularity locking)。如下图所示是一个四级的多粒度树。该树的根节点是数据库,表示最大的粒度;页节点是列,表示最小的粒度。

                                  


我们知道,选择锁定粒度时应该同时考虑并发度与开销两个因素,以求最优的效果。一般地,需要处理大量记录的事务可以以表为锁定粒度;需要处理多个表中的大量记录的事务可以以数据库为锁定粒度;而只需要处理某个表中少量记录的事务,则可以以记录为锁定粒度。

多粒度锁定协议是指,允许对多粒度树中的节点单独地加锁,另外,对一个节点加锁还意味着对这个节点的各级子节点也加同样的锁。  

因此,可以用两种方法对多粒度树中的节点加锁:显式锁定、隐式锁定。显式锁定是在事务中明确指定要加在节点上的锁;隐式锁定是由于在其上级节点中加显式锁时而使该节点获得的锁。

在多粒度锁定中,显式锁定与隐式锁定的作用与相容规则都是一样的。因此,当系统检查锁定的冲突时,不仅要检查显式锁定还要检查隐式锁定。

一般地,当对一个节点加锁时,系统第一要检查在该节点上有无显式锁定与之冲突;第二要检查其所有上级节点有无显式锁定与之冲突,以便查看在该节点上加该显 式锁定,是否会与从上级节点获得的隐式锁定有冲突;第三要检查所有下级节点有无显式锁定与之冲突,以便查看在该节点上加该显式锁定,是否会使下级节点从该 节点获得的隐式锁定与其显式锁定有冲突。

这种检查锁定冲突的方法的效率很低,所以引进了意向锁(Intended lock)的概念。意向锁

意向锁的含义是,如果对一个节点加某种意向锁,则会对该节点的各级下级节点加这种锁;如果对一个节点加某种锁,则必须先对该节点的各级上级节点加这种意向锁。

例如,对记录r1加锁时,必须先对它所在的表A加意向锁。于是,事务T1对表A加X锁时,系统只需要检查根节点数据库D和表A是否已经加了不相容的锁,而不再需要检查表A中每个记录是否加了X锁。

Oracle中的意向锁就是:Sub-Shared,Sub-Exclusive,Shared-sub-exlusive

Sub-Shared锁---意向共享锁

如果对一个节点加SS锁,则表示对它的所有下级节点有加S锁的意向;如果对一个节点加S锁,则必须先对该节点的各级上级节点加SS锁。

Sub-Exclusive锁---意向排他锁)

如果对一个节点加SX锁,则表示对它的所有下级节点有加X锁的意向;如果对一个节点加X锁,则必须先对该节点的各级上级节点加IX锁。

Shared-sub-exlusive---共享意向排他锁

如果对一个节点加SSX锁,则表示对它加S锁,然后再加SX锁,即SSX=S+SX。例如,如果事务T对表A加SSX锁,则表示事务T要读取整个表(S锁的作用),同时还会更新某些记录(SX锁的作用)。

各种锁之间的相容规则如下图表所示。                 


这些不同模式锁的保护能力顺序如下: Highest level    X>SSX>SX=S>SS>NULL   Lowest level

常见SQL锁加大锁模式如下表

SQL Statement

 Mode of Table Lock

SELECT...FROM   table ...

none

INSERT INTO   table   ...

RX

UPDATE   table   ...

RX

DELETE FROM   table   ...

RX

SELECT ... FROM   table      FOR UPDATE OF ...

RS

LOCK TABLE   table   IN    ROW SHARE MODE

RS

LOCK TABLE   table   IN    ROW EXCLUSIVE MODE

RX

LOCK TABLE   table   IN    SHARE MODE

S

LOCK TABLE   table   IN    SHARE ROW EXCLUSIVE    MODE     

SRX

LOCK TABLE   table   IN    EXCLUSIVE MODE

X


 RS: row share
 RX: row exclusive
 S: share
 SRX: share row exclusive
 X: exclusive

Enqueue算法
Lock使用的Enqueue算法,可以理解为“先入先出队列”。
注意: Enqueue的获取/转换/释放操作都是由 Session操作的,而不是由Process
如果会话的锁定请求不能满足,该会话的Lock Structure就被加到Waiter的链表的末端。当占用会话释放锁时,会检查Waiter和Converter队列,把锁分配给最先进入队列的请求者。 Waiter和Converter两个都是等待队列,二者的用法有细微的区别:如果某个操作先后需要两种不同模式的锁,比如先Share Mode然后是Exclusive Mode,则会话会先请求 Share Mode,获得后Lock Structure会挂在Owner队列上,当需要Exclusive Mode时,会话必须先释放Share Mode的锁,然后再次申请Exclusive Mode的锁,但是可能无法立即获得,这是这个请求被挂在Conveter队列下, Conveter队列会优先于Waiter 队列被处理。可以从v$lock视图中看到这些Lock信息,并且根据LMODE和REQUEST MODE判断谁是OWNER、 Waiter和 Conveter。
LMODE>0,REQUEST MODE=0        OWNER
LMODE=0,REQUEST MODE>0        Waiter

LMODE>0,REQUEST MODE>0        Conveter

DDL锁

当用户发布DDL(Data Definition Language)语句时会对涉及的对象加DDL锁。由于DDL语句会更改数据字典,所以该锁也被称为字典锁。

DDL锁能防止在用DML语句操作数据库表时,对表进行删除,或对表的结构进行更改。

对于DDL锁,要注意的是:

DDL锁只锁定DDL操作所涉及的对象,而不会锁定数据字典中的所有对象。

DDL锁由Oracle自动加锁和释放。不能显式地给对象加DDL锁,即没有加DDL锁的语句。

在过程中引用的对象,在过程编译结束之前不能被改变或删除,即不能被加排他DDL锁。

DDL锁的类型与特征如表所示。


数据记录的行级锁概述

数据块是Oracle最基本的存储单元,每个数据块可以分成两部分,数据块头和数据块体。数据块头存放着管理信息,和事务相关的就是 ITL(Interested Trasaction List)。数据块体存放着具体的记录。用户记录也按照一定的格式保存的,每条记录可以分成记录头和记录体两部分,记录头是描述信息的宽度,和事务相关的 是ITL Entry Pointer字段。整个行级锁共涉及以下4种结构。

ITL:每个数据块的块头都存有ITL用于记录哪些事务修改了这个数据块的内容。可以把它想象成一张表,每一个表项对应一个事务,包括事务号,事务是否提交等重要信息。

记录头ITL索引:每条记录的记录头部都有一个字段,用于记录ITL表项号,可以看作是指向ITL表的指针

TX锁:代表一个事务。属于LOCK机制拥有, Lock Structure、Resource Structure和Enqueue算法。

TM锁:也属于LOCK机制,用于保护对象的定义不被修改。

行级锁机制 当一个事务开始时,必须申请一个TX锁,这种锁保护的资源是回滚段、回滚数据块。因此申请也就意味着:用户进程必须先申请到回滚段资源后才开始一个事务,才能执行DML操作。申请到回滚段后,用户事务就可以修改数据了。具体顺序如下:

1、首先获得TM锁,保护事务执行时,其他用户不能修改表结构

2、事务修改某个数据块中记录时,该数据块头部的ITL表中申请一个空闲表项,在其中记录事务项号,实际就是记录这个事务要使用的回滚段的地址(应该叫包含)

3、事务修改数据块中的某条记录时,会设置记录头部的ITL索引指向上一步申请到的表项。然后修改记录。修改前先在回滚段将记录之前的状态做一个拷贝,然后修改表中数据。

4、其他用户并发修改这条记录时,会根据记录头部ITL索引读取ITL表项内容,确认是否事务提交。

5若没有提交,必须等待TX锁释放

从上面的机制来看,无论一个事务修改多少条记录,都只需要一个TX锁。所谓的“行级锁”其实也就是数据块头、数据记录头的一些字段,不会消耗额外的资源。 从另一方面也证明了,当用户被阻塞时,不是被某条记录阻塞,而是被TX锁堵塞。也正因为这点,很多人也倾向把TX锁称为事务锁。这里可通过实验来验证所说 结论。

会话1

SQL> select * from test;

        ID NAME

---------- --------

         1 A

         2 B

         3 C


SQL> savepoint a;

Savepoint created.


SQL> update test set name='ssss' where id=2;

1 row updated.

会话2

SQL> update test set name='ssdsdsds'where id=2;

会话1

SQL> rollback to a;

Rollback complete.

可以看到,虽然会话1已经撤销了对记录的修改,但是会话2仍然处于等待状态这是因为会话2是被会话1的TX锁阻塞的,而不是被会话1上的行级锁 阻塞(rollback to savepoint不会结束事务) 。
会话3
  SQL> select username,event,sid,blocking_session from v$session where username='HR';
USERNAME EVENT                                      SID BLOCKING_SESSION
-------- ----------------------------------- ---------- ----------------
HR       enq: TX - row lock contention              146              159
HR       SQL*Net message from client                159
会话1:
SQL> rollback;
会话2
SQL> update test set name='ssdsdsds'where id=2;
 1 row updated.
会话3
SQL> select username,event,sid,blocking_session from v$session where username='HR';
USERNAME EVENT                                      SID BLOCKING_SESSION
-------- ----------------------------------- ---------- ----------------
HR       SQL*Net message from client                159
事务结束,tx锁释放,会话2update执行成功。
其他类型的锁
HW锁:主要用来控制特定对象空间分配时的并发操作。v$lock中的id1为表空间编号ts#,id2为需要分配空间的对象segment header的相对DBA(Data Block Address)位置。争用主要发生在大量插入时,或手工对该对象allocate/deallocate空间时。
US锁:控制特定Undo Segment上的并行话操作。 v$lock中的id1为 Undo Segment的 编号*1,id2始终为1。触发US 锁的相关操作有:
对回滚段的create/drop/alter操作;Offining PENDING OFFUNE RBS by SMON;SMON-abortive offline cleanup;startup
CF锁:与控制文件相关的锁:1、串行化对控制文件的修改。2、任何涉及到对控制文件进行修改的进程都会持有此锁。 v$lock中的id1 始终为0 ,id2为0时表示串行化控制文件的操作,为1时表示共享信息的访问。使用场景如下:
日志切换
新增、删除各种类型的文件(数据文件,临时文件,日志文件)
打开、关闭、备份数据库,改变表空间、数据文件的在线状态,读写状态

转储控制文件中的对象信息(数据文件,日志文件)

修改数据库的checkpoint信息
JQ锁:此锁是为了控制对job的并发访问与执行。正在运行的job会持有此锁, v$lock中的id1 始终为0 ,id2为job的编号job_no,oracle以此为基础创建 dba_jobs_running视图,可通过该视图确定正在运行的任务
SQ锁:此锁用来防止多个进程同时刷新SGA中的Sequence缓存。 v$lock中的id1 为sequence对应的object_id ,id2始终为0。如果对Sequence的cache设置较小,就会导致系统遭遇较多的SQ enqueue等待。另外,如果sequence使用nocache,系统可能会遭遇到较严重的row cache lock,而不会出现SQ enqueue。

参考至:《大话Oracle RAC》张晓明著
           《让Oracle跑得更快》谭怀远著
​​​               http://blog.csdn.net/lxlj2006/article/details/5999563​

​              http://space.itpub.net/9268278/viewspace-196031​

​              http://www.dbthink.com/?p=575&utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+dbthink+%28a+db+thinker%27s+home%29​

本文原创,转载请注明出处、作者

如有错误,欢迎指正