Fingerprints of Latch——使用V$LATCH_MISSES调优Latch Wait

Latch等待应该说并不是一件坏事情。毕竟,设计Latch的目的就是用来让你等待的。Oracle使用Latch来保护SGA区中的关键数据结构不会在一个进程正在访问、修改时被另外一个进程访问,避免SGA区关键数据结构出现Corruption。暂时的waiting要比内存corruption更让人接受,不是么?不过,如果经常性的出现两个或多个进程等待同一个Latch,这就是一个问题了。因为,当Latch被释放(free)时,正在等待此Latch的多个进程将争抢Latch的所有权,而且只能有一个进程如愿获得Latch的所有权;而其他参与争抢的进程将消耗无效的CPU时间去spin、wait。随着参与争抢Latch的进程数量增加,将会造成严重的性能问题;而如果这个问题能够得到解决,这个性能问题会迅速的消失。一般来讲,共有三种不同的latch争用问题:1. 高CPU占用可能会触发latch争用问题。试想,如果一个已经获得latch所有权的进程不能迅速的获得CPU使用时间片,对这个latch的大量等待当然就不可避免了。为了避免由于CPU饥饿(CPU starvation)造成的latch争用问题,你应该保证你的系统不会持续较长时间的出现CPU占用率超过85%的压力高峰(当然,在某些系统中高CPU使用率是安全的)。很多系统问题(check for run-away process、paging)都会造成CPU的过度使用,所以说,在诊断一个Oracle性能问题的时候,你应该首先确保他确实是Oracle问题——而不是OS操作系统问题。2. Oracle预期latch被间断的、简短的占用。一些Latch争用性能问题的出现恰恰是因为一个Latch被超出预期的长时间占用。这种类型的问题通常是由于SGA区中的某个访问受latch保护的链表增长的太长(比如Shared Pool的碎片太多、buffer cache中的hash chains太长等等)。 这种类型的latch争用问题对_spin_count初始化参数的设置非常敏感。如果spin循环的时间恰恰短于这种类型的latch能够被获取的时间,这时尝试获取latch的进程就会停止spin,转入sleep状态;这种情况下,如果适当增加_spin_cout参数,使得spin循环的时间能够赶上占用Latch进程释放latch的时间,就可以避免进程sleep。当然,最好的调节方法是减少latch占用的时间,而不是增加latch spin的时间。3. Latch争用的性能问题也可能并不是由于Latch hold时间太长,而仅仅是因为获取Latch占用权的请求太过频繁了。最典型的一个例子是因为commit太过频繁而造成的’redo allocation’latch的争用。 这种情况下,最好的解决办法当然是避免太过频繁的latch request;但是很多情况下,最有效的解决方案也是最不可能的方案。通常,我们可以通过增加相应的child latch数量来缓解这个问题(比如 cache buffer lru latch),增加_spin_count对这种类型的性能问题没有帮助。如果我们不能降低latch request的频率(比如修改应用commit的频率),而且这个latch也不属于默认的long latch类别(默认好像只有两个latch是long latch: shared pool 、library cache);因为long latch默认支持latch posting特性,我们可以通过设置初始化参数_latch_wait_posting为2来强制对所有的latch类型都支持latch wait posting。 ——对于这一点,我心存疑虑,latch wait posting毫无疑问对某些latch会有负面影响,或者会过多的消耗资源;不然Oracle为什么只默认支持shared pool、libarary cache的wait posting呢;所以除非十分必要且经过测试,这种手段还是谨慎使用了。很显然的,要想恰当的解决latch争用造成的性能问题,首先我们得判断它是属于哪种类型的latch争用。以上3中类型中,CPU starvation应该是最容易判断的;其他两种类型的latch可以通过v$latch_misses加以区分。这个视图中包含了request或者hold每一个latch对应的Oracle Kernel位置。SQL> desc v$latch_misses Name Null? Type ----------------------------------------- -------- ---------------------------- PARENT_NAME VARCHAR2(50) WHERE VARCHAR2(64) NWFAIL_COUNT NUMBER SLEEP_COUNT NUMBER WTR_SLP_COUNT NUMBER LONGHOLD_COUNT NUMBER LOCATION VARCHAR2(64)SQL>WTR_SLP_COUNT字段记录进程在此内核位置request对应latch的次数,SLEEP_COUNT字段记录进程在此位置hold 对应latch而处于sleep状态的次数,LONGHOLD_COUNT字段记录因为一个latch在整个spin循环后仍然被其他进程hold,该进程转入sleep的次数。V$latch_misses中的计数统计信息并不完全精确,因为它也处于latch的保护下而不是被经常的更新。这个试图并不仅仅是提供信息用来区分是因为占用latch时间太长而造成争用还是因为latch request太过频繁造成的争用,它还提供了latch所处的Oracle Kernel精确位置。所以说,v$latch_misses中的数据 is an excellent fingerprint for 任何latch争用问题(呵呵,觉得翻译有困难)。下面是两个相关例子:1. 第一个案例中,存在对shared pool和library cache的争用。这个组合应该是很常见的了,但是这个案例中问题发生在kghupr1(Oracle Kernel location)这就比较少见了。在kghupr中,shared pool latch是用来unpin一个recreatable内存快,并把它添加到shared pool中某个lru队列中的;这个latch是绝对不会消耗太长时间的,因而,必然是出现了大量的对这个latch 的request导致了这个问题。而是什么导致了一个“小心”构建的应用系统——使用绑定变量的 更多的放弃内存快而不是重新使用它呢?我们所能想到的唯一可能就是:应用程序可能每次都是在再次执行游标前重新绑定(rebinding)这个游标。我们很快的询问了开发人员,确定了就是这个问题。所以,修改调整一下应用程序代码就解决问题了。2. 另一个案例中,争用的是library cache latch。V$latch_misses显示,在kglic处,这个latch被占用时间太长。在kglic中,library cache hash 链表和碎片队列将会被扫描,所以,看起来好像是由于library cache中的链表太长造成的,接着,我们在争用比较严重的时候dump library cache,但是检查发现,并没有较长的链表存在!所以只可能是CPU使用问题造成这个latch争用现象了。进一步检查,发现shared pool太大,导致部分shared pool被page out了,所以减少shared pool,问题解决。

Leave Comment