Oracle研发工程师为了保证Cache Fusion的各个实例一致性使用了超过70种的队列锁,12.2版本有超过90种队列。比如我们常见的HW,US,TX,TM,SS,LB等等。每一个版本的队列信息可以通过Oracle提供的v$lock进行查看。比如我们最常用的11gR2的版本展示
:
https://docs.oracle.com/cd/E11882_01/server.112/e40402/dynviews_2027.htm
。
Oracle的使用的队列大致可以分为三种类型:
1.实例类:比如实例启动和恢复,SCN同步;
2.事务类:比如library cache,dictionary cache,并行查询。
3.用户类:用户自定义的队列(enq:UL-contention)
当会话需要访问指定资源的时候,就需要获得锁结构(ksqlk),请求以特定锁模式获得对资源的访问。锁请求的锁结构属于如下三个链表之一(所有者,等待者和转换者)。可以根据v$lock视图的LMODE列和REQUEST列区分所有者,等待者还是转换者。平均等待时间可以通过v$enqueue_stat的cum_wait_time/total_wait#计算得到,单位为千分之一秒。
|
REQUEST |
ENQUEUE |
非零 |
零 |
所有者 |
非零 |
非零 |
转换者 |
零 |
非零 |
等待者 |
Oracle的本地队列的申请在ksq层完成即可,对于全局队列则需要经历ksi和kju层的申请。每一个队列资源和锁都对应分布锁管理的资源和锁。如果存在当前事务,则事务标识符(XID)是分布锁锁定标识的一部分(Oracle使用它来做死锁检测)。如果没有当前事务,锁定标识则使用连接线程号(2个字节),Oracle进程ID(2个字节)和ksuseq的标识符组成。对于每个Oracle进程而言,它们的ksuseq值始终以0开始然后递增。
ksq层始终使用XID调用ksi层以申请创建分布式锁。其他层(例如kqr或kqlm)在没有XID(进程拥有)的情况下调用ksi层,因而不能使用DLM的死锁检测功能。
锁模式
在Cache fusion中队列也可以视作一种资源,所以它会有不同模式的锁,如下图所示。细心的同学可以发现DLM lock和local lock的命名是不同的,Oracle称之为“某些历史”原因导致。
锁兼容性原则
1.相互兼容的锁可以同时存在于授权队列中。
2.请求队列上的锁与授予队列上的锁不兼容,并且与转换队列上的其他锁不兼容。
3.PR和CW组合存在特殊情况,PR与较小模式CW不兼容。这就不允许从PR到CW的锁降级。
4.GCS锁定模式是用下划线表示的。
锁存在于资源授予或转换队列中。如果锁模式发生更改,则它会在队列之间发生移动。授予队列中可以存在多个锁,但是它们必须是相互兼容的。相同模式的锁不一定与另一个相同模式兼容。在GES和GCS锁之间各种锁的兼容性矩阵不同。同队列的锁转换通常是降级,即转换为较小的模式。存在一些例外情况,稍后会介绍。
锁可以在以下任何条件下离开转换队列:
·进程请求锁定终止(删除锁定)。
·进程取消转换; 锁被移回到授权队列中以前的模式。
·请求的模式与Grant队列中最高级别的锁兼容,请求的锁在转换队列的前列(FIFO)并与先前的锁模式兼容。
1.
会话A尝试读取一个资源这时它会在Grant队列对资源添加授共享锁(一致性)。
2.这时会话B申请共享读锁定。因为共享锁是相互兼容的,因此它们可以同时驻留在Grant队列。
3.会话C申请共享读锁定,共享读锁被放置于Grant队列中。
4.会话A持有的共享锁转换为NULL模式。因为这是一个简单的降级,这种转换可以在Grant队列直接完成。
5.会话C尝试更改资源因此持有的锁尝试转换为独占模式,这时它必须放在转换队列。
由于转换队列是先进先出(FIFO),这种情况下可能就会产生死锁情况。
1.Grant队列上现在有两个共享的读锁和一个共享的写锁。
2.锁A尝试升级到独占模式。这个模式与B和C的模式不兼容,因此它被放置在转换上队列。旧模式的信息会保留防止转换操作取消的情况。
3.锁B尝试升级到受保护的读模式(不允许其他用户写)。此模式与C的模式不兼容,因此也会北放置到转换队列中。
4.锁C降级为NULL模式(对其他访问没有限制)。现在即使独占模式与NULL兼容,但锁定A无法完成转换,因为与锁B的旧共享读取模式不兼容。锁B可以完成转换,但它位于锁A后面。