
导读:
Latch是Oracle数据库中一个非常重要的轻量级内存锁,它是一个复杂且非常重要的概念,涉及到操作系统、Oracle内存结构、工作原因以及性能优化等多个方面的知识,这里以读书笔记的形式,通过一个知识系列,来全面学习Latch的工作原理。
本文是第二部分,介绍Latch工作原理:

Bus line:
Bus line又称为总线,它是操作系统中所有CPU共享的与内存进行交互的通道。
当一个CPU发出spin命令时候,就会锁住总线,以阻止其它CPU对内存的访问与操作。
这就是说CPU可以通过spin命令来实现对共享Bus line的排它控制。
操作系统的Spin函数:
该函数将使CPU在内存中对某个数值的计算原子化,也就是若CPU对某个数值进行计算时,其它的CPU将不能再对该值进行访问。Spin函数的实现原理就是利用操作系统的Bus line总线功能,当CPU要对某个值进行运算时,它首先读取该数值到内存。然后锁住Bus line总线,使其它的CPU不能再对该数值进行运算。从而实现了对该数值的绝对控制。
Latch的工作原理:
Latch在本质上就是一个标示符,只是Oracle对这个标示符的控制是通过操作系统的Spin函数实现的。拥有Latch实际上就是拥有对Latch的控制权,但对Latch的控制绝不是为是控制Latch本身,而是为了控制Latch所保护的对象。
举例说:目前有一个CBC链条,为了实现对它的保护,我们为其设置了一个Latch,当Latch的Flag值为Y表示正被某个进程使用,若值为N表示未被使用。这就是Latch其实就是一个Flag标示。
现在一个进程P1想要访问CBC链条,它必须首先获取该CBC链条的Latch。于是,该P1所在的CPU将该Latch的Flag值置为Y,并注明它正在被P1所有。如果在同一时刻,另一个进程P2也想要获得这个Latch,它也需要使用它所在的CPU控制它。在Latch中,Oracle使用的正是操作系统的Spin函数,也就是说在同一时刻,只能有一个CPU可以控制该Latch的Flag变量。因而,可以实现对Latch的唯一控制。
如果没有Spin函数的这一特性,就有可能使同一时刻有多个CPU对Latch的控制。简言之,Latch就是一个标示符,表明当前是否被哪个进程使用。为了实现进程对其的唯一控制,Oracle调用了操作系统的Spin函数,从而实现对该标示符的原子性操作。只有控制了某资源的Latch才能实现对某个资源的控制。
Latch需要并发控制:
所有共享的资源必定会有竞争,有竞争则必然要进行并发控制。在Oracle中,Latch本身也是一种共享资源。因此,对它的访问也需要进行并发控制。Latch其实就是一种Flag标示符,如果Flag标志为N,且同时间有两个进程P1与P2在并行运行着,并希望获得该Latch。
如果P1、P2同时要用到Flag,于是P1先将Flag从内存读入,检测Flag是N,于是准备将Flag置为Y。在P1将Flag写回内存前,P2也将Flag读入,此时Flag仍然是N。如是P2也发出命令将Flag置Y。这样,P1,P2都认为是自己成功将Flag置为Y的,拥有对资源的操作权,链表被破坏了。
获取Latch:
任何时候,任何进程想要访问Latch保护的对象,其最先必须做的就是获取该保护对象的Latch。在9i之前,其基本过程是请求-Spin-休眠-请求-Spin-休眠...占用。
Spin:
如果进程因为别的进程正占用目标对象而无法获得Latch时,他会对CPU进行一次Spin(旋转)。这个时间非常的短暂,spin过后它将继续获取,若仍不成功则仍然spin,直到spin次数到达阀值限制(这个由隐含参数_spin_count指定)。
休眠:
若在进行多次spin后,进程仍未得到目标对象的Latch,它会进行短期的休眠。休眠过后会继续spin,直到获取目标对象的Latch为止。进程休眠的时间也是存在算法的,他会随着spin次数而递增,以厘秒为单位,如1、1、2、2、4、4、8、8...。休眠的阀值限制由隐含参数_max_exponential_sleep控制,默认是2秒。针对不同的情况,休眠可以分为短期等待和长期等待。
短期等待:
如果当前进程已经占用了别的Latch,就是说它要同时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资源检查:
如果一个进程请求、旋转、休眠Latch用了很长时间,他会通知PMON进程,查看Latch的占用进程是否已经意外终止或死亡,如果是则PMON会清除释放占用的Latch资源。
什么要SPIN:
如果没有SPIN,它就意味着请求进程在得到数据块的Latch时,就必须暂时的放弃当前CPU,并进行上下文切换(context switch)。这样CPU就必保存当前进程运行时的一些状态信息,比如堆栈、信号量等数据结构。然后在再次请求块的Latch时引入进程的状态信息,处理完后再切换回原来的进程状态。这个过程如果频繁的发生在一个高事务,高并发进程的处理系统里面,将是个很昂贵的资源消耗。
所以Oracle选择了spin,让进程继续占有CPU,运行一些空指令,之后继续请求,继续spin,直到达到_spin_count值。这时会放弃CPU,进行短暂的休眠,再继续刚才的动作。简单地说,进程在没有获取到想要的Latch时,它不会停止对当前CPU的占有,而是让其执行。





