如果经过一个完整的旋转,这个Latch仍然不可以用,那个请求进程就会首先放弃对cpu的请求而去sleep。一个sleep就对应一个Latch free的等待事件。开始,这个sleep时间会是一个厘秒(one centisecond)。这个时间会以双指数的增长方式去增加直到参数_MAX_EXPONENTIAL_SLEEP所规定的,一般是2秒。通常调整_SPIN_COUNT这个值没有什么大的作用,过度的latch竞争通常都是差的程序设计的结果。此外就是我前面提到的_SPIN_COUNT只对多CPU的系统有用。这个参数只会在其他tunning方法已经用完,而又有足够的CPU带宽时,才考虑增加这个值。
通常latch只能被短暂的拥有,成功的spin能够节省CPU时间,当然这个只对多CPU环境有意义。这里有人会问为什么要SPIN,为什么不直接休眠等待?这里要明白休眠意味着什么,他意味着暂时的放弃CPU,进行上下文切换(context switch),这样CPU要保存当前进程运行时的一些状态信息,比如堆栈,信号量等数据结构,然后引入后续进程的状态信息,处理完后再切换回原来的进程状态,这个过程如果频繁的发生在一个高事务,高并发进程的处理系统里面,将是个很昂贵的资源消耗,所以他选择了spin,让进程继续占有CPU,运行一些空指令,之后继续请求,继续spin,直到达到_spin_count值,这时会放弃CPU,进行短暂的休眠,再继续刚才的动作,Oracle软件就是这么设计的。
Latch的获取,可以说遵从下面的规律Fast get –spin—sleep—spin—sleep—spin—sleep--,所以有两种情况,其中一个是Latch能以no-wait(no spin or sleep)的模式去获取。如有很多的redo copy latches,如果一个进程需要这个latch,它首先是以no-wait模式去请求。如果一个进程第一次获取这些子latch中的任何一个失败,它会立即使用no-wait模式询问下一个。只有当采用no-wait模式试图获取所有的子latch都失败以后,才会转而采用willing-to-wait模式。另外一个是借助level rule,每个latch都有自己的level。为了防止死锁,请求者只能请求level比现在拥有者高的latch。如果这个latch的请求是一个低的level,那么其中的一个方法就是释放现在被占有的latch,然后请求者以一个适当的顺序去获取。另外一种就是willing-to-wait模式,大多数latch都是短等待latch,所以,进程请求latch时不会等待太长的时间。Oracle进程请求latch失败而导致进入睡眠状态,每次睡眠时间按双指数队列增长,比如睡眠时间可能像下面的队列一样:1,1,2,2,4,4,8,8,16,16,32,32,64,64(厘秒)……,最长的睡眠时间由隐含参数_max_ exponential_sleep,默认2秒。但是如果一个进程当前已经持有其他的latch,则最长睡眠时间会减少为_max_sleep_holding_latch,默认值4厘秒。这样,如果一个进程已经持有latch,就不允许睡眠太长的时间,否则可能会使其他等待该进程所持有的latch的进程的等待时间过长。
如果请求该latch的进程进入睡眠状态,需要其他进程来唤醒,这会产生一个latch wait posting等待事件,该行为由隐含参数_latch_wait_posting控制。在oracle8i,只有2个长等待latch,_latch_wait_posting参数从oracle9i起已经废弃,使用latch wait posting的latch的统计信息被记录在waiters_woken列中,这个数值就等于唤醒Post的次数。这个只是对于长等待latch而言。这里也要引入一个概念锁存器等待链表latch wait list。Latch的释放机制需要唤醒第一个为次latch而等待进程。比如说library chace、shared pool、library cache load lock。那些为此而等待进程是由一个串连waiter的链表(锁存器等待链表latch wait list)所保护。如果占有latch的进程死了,PMON进程会清理该进程和释放latch。如果这个占有者还活着,那就存在latch争用的情况或者操作系统调度有问题。
----------------------------------------------待续-----------------------------------------------------
星期四, 五月 24, 2007
订阅:
博文评论 (Atom)
没有评论:
发表评论