一、锁的概念
1、概念:锁是并发访问的时候用于保护不共享资源不被同时并发修改的机制。
2、解释:
2.1、数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。
2.2、加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。
2.3、在Oracle 数据库中,它并不是对某个表加上锁或者某几行加上锁,锁是以数据块的一个属性存在的。 也就是说,每个数据块本身就存储着自己数据块中数据的信息,这个地方叫ITL(Interested Transaction List),凡是在这个数据块上有活动的事务,它的信息就会记录在这里面供后续的操作查询,保证事务的一致性。
2.4、在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。
二、锁的框架
1、LOCK框架:Resource Structure(资源)、Lock Structure(锁)和Enqueue(排队机制)。Resource和Lock是数据结构,Enqueue是算法。
1.1、Resorce Structure(资源):
Oracle对于每个需要进行"并发控制"的资源,都在SGA中用一个数据结构来描述它,这个数据结构叫作Resource Structure。这个数据结构中有3个与"并发控制"有关的成员:Owner,Waiter和Converter。这是3个指针,分别指向3个由Lock Structure组成的链表。
1.2、Lock Structure(锁):
每个进程要访问共享资源时,必须要先“锁定”该资源。这个动作实际就是从内存中申请一个Lock Structure,在其中记录”锁模式,进程ID“等重要信息。然后看是否能立即获得访问权,如果不能,则把Lock Structure挂到Resource Structure的Waiter链表中。如果能获得访问权,则把 Lock Structure挂到Resource Structure的 Onwer 链表中。上面说到的 Resource Structure中的 Onwer,Waiter,Converter三个成员就是指向由Lock Structure组成的链表的指针。Lock的组成示意图如下:
1.3、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
三、锁的分类
1、DML lock(data locks,数据锁):用于保护数据的完整性;
2、DDL lock(dictionary locks,字典锁):用于保护数据库对象的结构(例如表、视图、索引的结构定义)
3、Internal locks和latches(内部锁与闩):保护内部数据库结构;
4、Distributed locks(分布式锁):用于OPS(并行服务器)中。
5、PCM locks(并行高速缓存管理锁):用于OPS(并行服务器)中。
四、锁的分类介绍
1、DML锁:
DML锁的目的在于保证并发情况下的数据完整性。在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。当我们在对表的数据进行DML时,不仅会在数据行的头部记录行级锁,而且还会在表的级别上添加一个表级锁。那么当另一个用户要删除表时,发现该表上具有一个表级锁,于是等待。
Oracle中的对象可以分为两类,简单对象(Simple Objects)和复合对象(Compound Objects)。数据表是典型的复合对象,表中的每条记录是典型的简单对象。对于简单对象只有3中模式:Null、Share、Exclusive。而 对于复合对象,除了 Null、Share、Exclusive这3中模式外,还有 Sub-Shared、Sub-Exclusive、Shared-sub-exclusive。
1.1、TM锁的类型:
1、行级共享锁SS(lmode=2,Row Share Table Lock,RS)
通常是通过select……from for update语句添加的,同时该方法也是我们用来手工锁定某些记录的主要方法。比如:当我们在查询某些记录的过程中,不希望其他用户对查询的记录进行更新操作,则可以发出这样的语句。当数据使用完毕后,直接发出rollback命令将锁定解除,当表上添加了RS锁定以后,不允许其他事物对相同的表添加排他锁,但是允许其他的事物通过DML语句或lock命令锁定相同表里的其他数据行。当需要以Share模式访问复合对象中的成员时,需要对复合对象加这种模式的锁,而对成员加Shared模式的锁;这种模式有不同的缩写(SS,RS,CR)。
2、行级排他锁SX(lmode=3,Row Exclusive Table Lock,RX)
当我们进行DML时会自动在被更新的表上添加RX锁,或者也可以通过执行lock命令显式的在表上添加RX锁。在该锁定模式下,允许其他的事物通过DML语句修改相同表里的其他数据行,或通过lock命令对相同表添加RX锁定,但是不允许其他事物对相同的表添加排他锁(X锁)。当要以Exclusive模式访问复合对象中的成员时,需要对复合模式对象加这种模式的锁,而对成员加Exclusive模式的锁;这种锁模式有不同的缩写(SX,RX,CR)。
3、共享锁S(lmode=4,Share Table Lock,S)
通过lock table in share mode命令添加该S锁。在该锁定模式下,不允许任何用户更新表。也不允许其他用户发出select ……from for update命令对标添加RS锁。拥有者本身对资源进行只读访问,允许其他用户并发只读访问。
4、共享行级排他锁SSX(lmode=5,Share Row Exclusive Table Lock,SRX)
通过lock table in share row exclusive mode 命令添加SRX锁。该锁定模式比行级排它锁和共享锁的级别都要高,这时不能对相同的表进行DML操作,也不能添加共享锁。当需要对复合对象的成员进行Exclusive模式访问,同时要对复合对象本身进行Shared模式访问时,需要对复合对象加这种模式的锁;这种模式的锁有不同的缩写(SSX,SRX,PW)。
5、排他锁X(lmode=6,Exclusive,X)
通过lock table in exclusive mode命令添加X锁。在该锁定模式下,其他用户不能对表进行任何的DML和DDL操作,该表上只能进行查询。拥有者本身对资源进行修改访问,不允许其他用户任何并发访问。
锁对应类型表:
锁对应兼容性:
1.2、TX锁:
修改数据的事务在执行期间会获得这种锁(一看到TX锁,一般人会认为他是一个行级排他锁,但这个是不正确的,其实TX是一个事务锁,他无论处理了多少行,都只是这一个事务锁在起作用,在v$lock视图中这个锁的id1,id2(这两个值也同时告诉了你,这些数据在回滚段中的位置,这样当另一个事务来读取,却发现别人在用不能读,他就同个这个TX锁的id到回滚段中找到备份的数据读取出来)的值是不会变化的。这个事务锁会在行级对数据产生影响,比如阻塞。在一个主键表中,我们更新一个主键值是,就会产生一个TX事务锁,他阻止其他人对这一行进行更新)。
在 Oracle 的每行数据上,都有一个标志位来表示该行数据是否被锁定。Oracle不象其它一些DBMS(数据库管理系统)那样,建立一个链表来维护每一行被加锁的数据,这样就大大减小了行级锁的维护开销,也在很大程度上避免了其它数据库系统使用行级封锁时经常发生的锁数量不够的情况。数据行上的锁标志一旦被置位,就表明该行数据被加X锁,Oracle在数据行上没有S锁。
数据记录的行级锁概述:
数据块是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锁、每个表一个的TM锁,内存开销非常小。而所谓的"行级锁"其实只是数据块头、数据记录头的一些字段,不会消耗额外资源。
3、DDL锁:
当用户发布DDL(Data Definition Language)语句时会对涉及的对象加DDL锁。由于DDL语句会更改数据字典,所以该锁也被称为字典锁。
DDL锁能防止在用DML语句操作数据库表时,对表进行删除,或对表的结构进行更改。对于DDL锁,要注意的是:
DDL锁只锁定DDL操作所涉及的对象,而不会锁定数据字典中的所有对象。
DDL锁由Oracle自动加锁和释放。不能显式地给对象加DDL锁,即没有加DDL锁的语句。
在过程中引用的对象,在过程编译结束之前不能被改变或删除,即不能被加排他DDL锁。
DDL锁的类型与特征如表所示:
4、Internal锁和latches锁:
4.1、串行化概述
数据库系统本身是一个多用户并发处理系统,在同一个时间点上,可能会有多个用户同时操作数据库, 多个用户同时在相同的物理位置上写数据时,不能发生互相覆盖的情况,这叫做串行化,串行化会降低系统的并发性,但这对于保护数据结构不被破坏来说则是必需的。在Oracle数据库中,通过闩锁(latch)和锁定(lock)来解决这两个问题。
闩锁和锁定既有相同点又有不同点。相同点在于它们都是用于实现串行化的资源。而不同点则在于闩锁(Latch)是一个低级别、轻量级的锁,获得和释放的速度很快,以类似于信号灯的方式实现。而锁定(Lock)则可能持续的时间很长,通过使用队列,按照先进先出的方式实现。也可以简单地理解为闩锁是微观领域的,而锁定则是宏观领域的。
latch是用于保护SGA区中共享数据结构的一种串行化锁定机制。它不仅仅用于buffer cache, 还用于shared pool以及log buffer等。
4.2、闩锁(latch)概述
Oracle数据库使用闩锁(latch)来管理SGA内存的分配和释放.Latch是用于保护SGA区中共享数据结构的一种串行化锁定机制。Latch的实现是与操作系统相关的,尤其和一个进程是否需要等待一个latch、需要等待多长时间有关。
Latch是一种能够极快地被获取和释放的锁,它通常用于保护描述buffer cache中block的数据结构。与每个latch相联系的还有一个清除过程,当持有latch的进程成为死进程时,该清除过程就会被调用。Latch还具有相关级别,用于防止死锁,一旦一个进程在某个级别上得到一个latch,它就不可能再获得等同或低于该级别的latch。
Latch 不会造成阻塞,只会导致等待。阻塞是一种系统设计上的问题,等待是一种系统资源争用的问题。
4.3、spin概念
比如数据缓存中的某个块要被读取,我们会获得这个块的 latch,这个过程叫做spin,另外一个进程恰好要修改这个块,他也要spin这个块,此时他必须等待,当前一个进程释放latch后才能spin 住,然后修改,如果多个进程同时请求的话,他们之间将出现竞争,没有一个入队机制,一旦前面进程释放所定,后面的进程就蜂拥而上,没有先来后到的概念,并且这一切都发生的非常快,因为Latch的特点是快而短暂。
4.4、spin与休眠:
休眠意味着暂时的放弃CPU,进行上下文切换(context switch),这样CPU要保存当前进程运行时的一些状态信息,比如堆栈,信号量等数据结构,然后引入后续进程的状态信息,处理完后再切换回原来的进程 状态,这个过程如果频繁的发生在一个高事务,高并发进程的处理系统里面,将是个很昂贵的资源消耗,所以Oracle选择了spin,让进程继续占有 CPU,运行一些空指令,之后继续请求,继续spin,直到达到_spin_count值,这时会放弃CPU,进行短暂的休眠,再继续刚才的动作。
oracle中,latch是一种轻量级的锁。一般来说,latch由三种内存元素成:pid(进程id),内存地址和内存长度。Latch保证对共享数 据结构的排它性访问,以此来保证内存结构的完整性不受到损坏。在多个会话同时修改或者检视(inspect)sga中同一个内存结构时,必须串行化访问以 保证sga中数据结构的完整性。
4.5、Latch的种类
愿意等待(Willing-To-Wait)
大部分的latch都属于这种类型(Willing-To-Wait)。这种类型的latch都是通过Test-And-Set的方式来实现的。
任何时候,只有一个进程可以访问内存中的某一个数据块,如果进程因为别的进程正占用块而无法获得Latch时,他会对CPU进行一次spin(旋转),时间非常的短暂,spin过后继续获取,不成功仍然spin,直到spin次数到达阀值限制(这个由隐含参数_spin_count指定),此时进程会停止spin,进行短期的休眠,休眠过后会继续刚才的动作,直到获取块上的Latch为止。进程休眠的时间也是存在算法的,他会随着spin次数而递增,以厘秒为单位,如1,1,2,2,4,4,8,8,。。。休眠的阀值限制由隐含参数_max_exponential_sleep控制,默认是2秒,如果当前进程已经占用了别的Latch,则他的休眠时间不会太长(过长会引起别的进程的Latch等待),此时的休眠最大时间有隐含参数_max_sleep_holding_latch决定,默认是4厘秒。这种时间限制的休眠又称为短期等待。
另外一种情况是长期等待锁存器(Latch Wait Posting),此时等待进程请求Latch不成功,进入休眠,他会向锁存器等待链表(Latch Wait List)压入一条信号,表示获取Latch的请求,当占用进程释放Latch时会检查Latch Wait List,向请求的进程传递一个信号,激活休眠的进程。Latch Wait List是在SGA区维护的一个进程列表,他也需要Latch来保证其正常运行,默认情况下share pool latch和library cache latch是采用这个机制。
如果将隐含参数_latch_wait_posting设置为2,则所有Latch都采用这种等待方式,使用这种方式能够比较精确的唤醒某个等待的进程,但维护Latch Wait List需要系统资源,并且对Latch Wait List上Latch的竞争也可能出现瓶颈。
如果一个进程请求,旋转,休眠Latch用了很长时间,他会通知PMON进程,查看Latch的占用进程是否已经意外终止或死亡,如果是则PMON会清除释放占用的Latch资源。总之, Latch获取的流程:请求-SPIN-休眠-请求-SPIN-休眠 ... ... 占用。
不等待(No-Wait)
这种类型的latch比较少,对于这种类型的latch来说,都会有很多个可用的latch。当一个进程请求其中的一个latch时,会以no-wait模式开始请求。如果所请求的latch不可用,则进程不会等待,而是立刻请求另外一个latch。只有当所有的latch都不能获得时,才会进入等待。
latch的获取过程可以用下图来概括:
4.6、Latch和Lock的区别
Latch是对内存数据结构提供互斥访问的一种机制,而Lock是以不同的模式来套取共享资源对象,各个模式间存在着兼容或排斥,从这点看出,Latch 的访问,包括查询也是互斥的,任何时候,只能有一个进程能pin住内存的某一块,幸好这个过程是相当的短暂,否则系统性能将没的保障。
Latch只作用于内存中,他只能被当前实例访问,而Lock作用于数据库对象,在RAC体系中实例间允许Lock检测与访问。
Latch是瞬间的占用,释放,Lock的释放需要等到事务正确的结束,他占用的时间长短由事务大小决定。
Latch是非入队的,而Lock是入队的。
Latch不存在死锁,而Lock中存在。
4.7、Latch的cleanup
在latch的使用过程中,可能会出现一些异常,而导致有些latch被异常占有得不到释放,这样就会有问题了,别的进程过来请求不到。出现这样的异常pmon进程会跟进处理,对于其处理的流程来说,最重要的莫过于将没有提交的事物回滚,那么就需要latch支持恢复,那么latch在开始操作前会先写一些信息去latch的恢复区。Pmon 3秒钟会自动运行一下,但是这也是很长的一段时间了,所以在进程在请求一个latch失败多次之后,会post pmon进程去check一下占有这个latch的process是不是正常。
4.8、Latch的level
Latch的级别分为0~14,共15个级别,0级最低,14最高。如果两个latch之间有联系,只能请求level更高的latch。原因如下:
如果a进程占有一个level 为5的latch,它去请求一个level为3的latch,而进程b,占有这个level为3的latch,又去请求那个level 为5的latch,这样会有什么问题呢?因为它是可以去spin的,又是可以去sleep的,sleep之后还是继续重复,那就永远没有完没有了了。所以呢,level的request是有level顺序的,不能随便的请求,只能由低级的latch去请求高级的latch。
如果经常a一定要申请进程b的latch的话,只能放弃原有latch level5为的latch重新对b进程的latch进行申请。
4.9、Latch资源争用
如果latch资源被争用,通常都会表现为CPU资源使用过高。
而反过来说,如果我们发现CPU资源很紧张,利用率总是在90%以上,甚至总是在100%,其主要原因有以下几点。
1、SQL语句 如果没有使用绑定变量,很容易造成频繁读写shared pool里的内存块,如果存在大量的SQL被反复分析,就会造成很大的Latch争用和长时间的等待, 从而导致与解析SQL相关的共享池中的Latch争用 。与 shared pool共享池相关的latch有Library Cache Latch 和Shared Pool Latch。如果数据库出现了上述latch的争用,则有必要检查下是否有正确使用绑定变量
2、 访问频率非常高的数据块被称为热快(Hot Block),当很多用户一起去访问某几个数据块时,就会导致 数据缓冲池Latch 争用,最常见的latch争用有 :buffer busy waits和cache buffer chain
Cache buffer chian:
当一个会话需要去访问一个内存块时,它首先要去一个像链表一样的结构中去搜索这个数据块是否在内存中,当会话访问这个链表的时候需要获得一个Latch,如果获取失败,将会产生Latch cache buffer chain 等待,导致这个等待的原因是访问相同的数据块的会话太多或者这个列表太长(如果读到内存中的数据太多,需要管理数据块的hash列表就会很长,这样会话扫描列表的时间就会增加,持有chache buffer chain latch的时间就会变长,其他会话获得这个Latch的机会就会降低,等待就会增加)。
Buffer busy waits:
当一个会话需要访问一个数据块,而这个数据块正在被另一个用户从磁盘读取到内存中或者这个数据块正在被另一个会话修改时,当前的会话就需要等待,就会产生一个buffer busy waits等待。
产生这些Latch争用的直接原因是太多的会话去访问相同的数据块导致热快问题,造成热快的原因可能是数据库设置导致或者重复执行的SQL 频繁访问一些相同的数据块导致。热块产生的原因不尽相同,按照数据块的类型,可以分成以下几种热块类型,不同热块类型处理的方式都是不同的:表数据块、索引数据块、索引根数据块和文件头数据块。
3、另外也有一些latch等待与bug有关,应当关注Metalink相关bug的公布及补丁的发布。为何latch的争用会引起CPU使用率较高呢?
其实很容易理解,比如进程A持有latch,此时进程B也需要持有相关latch,但是没有获得,这时候进程B就需要进行spin,如果类似进程B的进程较多的话,对CPU进行spin的进程就会较多,表现也就是CPU利用率非常高,但是吞吐量却很低,典型的“出工不出活”。
五、锁定的粒度与意向锁
锁定对象的大小被称为锁定的粒度(granularity)。锁定对象可以是逻辑单元(如数据库、表、记录、列、索引等),也可以是物理单元(如数据页、索引页等)。
锁定的粒度与系统的并发度和并发控制的开销密切相关。一般地,锁定的粒度越大,需要锁定的对象就越少,可选择性就越小,并发度就越小,开销就越小;反之,锁定的粒度越小,需要锁定的对象就越多,可选择性就越大,并发度就越大,开销就越大。
例如,如果锁定的粒度是表,事务T1需要操作表A中的记录r1,则T1必须对包含记录r1的表A加锁。在T1对A加锁之后,事务T2需要操作表A中的记录r2,因此就需要对表A加锁,但此时因为T1已经在表A上加了锁,所以T2的加锁就会失败,就被迫等待,直到T1释放锁为止,事务T1和T2就不能并发执行了;如果锁定的粒度是记录,则事务T1可以对表A中的记录r1加锁,同时事务T2也可以对表A中的记录r2加锁,所以事务T1和T2就可以并发执行了,并发度就增大了,但维护这个并发度就需要更多的开销了。
1、多粒度锁定与多粒度树
如果在一个数据库管理系统中,同时支持多种锁定粒度供事务选择,这种锁定方法就被称为多粒度锁定(multiple granularity locking)。
如下图所示是一个四级的多粒度树。该树的根节点是数据库,表示最大的粒度;页节点是列,表示最小的粒度。
从前面的例子可知,选择锁定粒度时应该同时考虑并发度与开销两个因素。以求最优的效果。一般地,需要处理大量记录的事务可以以表为锁定粒度;需要处理多个表中的大量记录的事务可以以数据库为锁定粒度;而只需要处理某个表中少量记录的事务,则可以以记录为锁定粒度。
多粒度锁定协议是指,允许对多粒度树中的节点单独地加锁,另外,对一个节点加锁还意味着对这个节点的各级子节点也加同样的锁。
因此,可以用两种方法对多粒度树中的节点加锁:显式锁定、隐式锁定。显式锁定是在事务中明确指定要加在节点上的锁;隐式锁定是由于在其上级节点中加显式锁时而使该节点获得的锁。
在多粒度锁定中,显式锁定与隐式锁定的作用与相容规则都是一样的。因此,当系统检查锁定的冲突时,不仅要检查显式锁定还要检查隐式锁定。
一般地,当对一个节点加锁时,系统第一要检查在该节点上有无显式锁定与之冲突;第二要检查其所有上级节点有无显式锁定与之冲突,以便查看在该节点上加该显式锁定,是否会与从上级节点获得的隐式锁定有冲突;第三要检查所有下级节点有无显式锁定与之冲突,以便查看在该节点上加该显式锁定,是否会使下级节点从该节点获得的隐式锁定与其显式锁定有冲突。
这种检查锁定冲突的方法的效率很低,所以引进了意向锁(Intendedlock)的概念。
2、意向锁
意向锁的含义是,如果对一个节点加某种意向锁,则会对该节点的各级下级节点加这种锁;如果对一个节点加某种锁,则必须先对该节点的各级上级节点加这种意向锁。
例如,对记录r1加锁时,必须先对它所在的表A加意向锁。于是,事务T1对表A加X锁时,系统只需要检查根节点数据库D和表A是否已经加了不相容的锁,而不再需要检查表A中每个记录是否加了X锁。
有如下几种意向锁。
一、IS锁(IntendedShare lock,意向共享锁)
如果对一个节点加IS锁,则表示对它的所有下级节点有加S锁的意向;如果对一个节点加S锁,则必须先对该节点的各级上级节点加IS锁。
二、IX锁(IntendedeXclusive lock,意向排他锁)
如果对一个节点加IX锁,则表示对它的所有下级节点有加X锁的意向;如果对一个节点加X锁,则必须先对该节点的各级上级节点加IX锁。
三、SIX锁(ShareIntended eXclusive lock,共享意向排他锁)
如果对一个节点加SIX锁,则表示对它加S锁,然后再加IX锁,即SIX=S+IX。例如,如果事务T对表A加SIX锁,则表示事务T要读取整个表(S锁的作用),同时还会更新某些记录(IX锁的作用)。
包括意向锁的各种锁之间的相容规则如表所示。
例如,当事务T对表A保持的锁是S锁时,事务Ti不可以获得对表A的IX锁,但可以获得对A的IS锁。所以S锁与IX锁的强度相当,但比IS锁的强度要强。
同上理,可以逐个地推导出这些锁的强度关系,如图所示。图中上面的锁比下面的锁的强度要强,同级的锁强度相当。一个事务在申请锁定时以强锁代替弱锁是安全的(但会降低并发度),反之则不然。申请锁定时应该按自上而下的次序进行,释放锁定时则应该按自下而上的次序进行。
具有意向锁的多粒度锁定方法能提高系统的并发度,减少加锁和解锁的开销,已经在实际DBMS中得到应用,如Oracle数据库。
本文参考至:
《让Oracle跑得更快》谭怀远著
《大话Oracle RAC:集群 高可用性 备份与恢复》 张晓明著
本文出自 “造烛求明读书求理” 博客,请务必保留此出处http://mgwilliam.blog.51cto.com/6110423/1412671
原文地址:http://mgwilliam.blog.51cto.com/6110423/1412671