Oracle有多种锁机制,主要有:latch,enqueue,分布式锁,全局锁(用于并行实例执行)
本文主要讨论latch的内容,Oracle如何产生latch,以及如何导致latch争用。
1. 什么是latch
2. latch和enqueue的对比
3. 何时需要获得latch
4. latch请求模式
5. 什么原因导致latch争用
6. 如何确认内部latch的争用
7. 检查latch信息的SQL脚本
8. 列出所有的latch
9. 列出DBA需要关心的latch
10. 调整 _SPIN_COUNT参数(在Oracle7中是 _LATCH_SPIN_COUNT)
1. 什么是latch
latch是一种低级的序列化机制,用于保护SGA中的共享数据结构。依靠系统来操作latch的执行,尤其是关于一个进程是否需要等待latch以及需要等待多久。
latch锁的特点是它的请求和释放都很快。latch主要目的是为了避免多个进程在某一时刻执行了相同的代码片段。当一个进程僵死长时间持有latch,latch就会调用清除程序。latch有关联级别,用于防止死锁。一旦一个进程在某一级别请求了latch,后续的进程不能请求相同级别或更低级别的latch(除非用nowait方式请求)。
2. latch和enqueue的对比
enqueue是ORACLE另一种类型的锁机制。enqueue具有更复杂的机制,可以允许多个并发进行在不同程度上共享已知的资源。任何一个对象在并发使用时,都被enqueue保护。它的一个好处,例如在表上加锁,允许多种级别共享表。例如2个进程可以同时用share mode锁表,或share update mode方式锁表。
latch和enqueue的第一个不同是,enqueue是通过OS特殊的锁机制来获得。enqueue允许用户在请求锁时将请求模式储存到锁中。如果进程请求的锁的mode与锁上的mode互斥,则不能获得该锁,该请求需要等待。OS将请求进程放到一个等待队列中,该队列是先进先出的。
latch和enqueue的另一个不同是,latch没有像enqueue那样的等待队列,latch等待通过时间器调度被唤醒,再次尝试请求或spin(只在多处理器环境中)。如果所有的等待者都同时再次尝试请求(依赖于调度器),任何一个都可能获得latch,所以有可能会造成,最早请求latch的进程最后获得该latch。
3. 何时需要获得latch
一个进程需要在SGA(System Global Area)的某个数据结构中运行时,需要请求latch。在该数据结构中执行的这段时间,都持有该latch。当进程在该数据结构中执行完成之后,删除该latch。每个latch都保护了不同的数据结构,通过latch名来识别。
Oracle使用原子指令,类似测试并置位操作(test and set),对latch进行操作。如果latch被其他进程获得,那么本次请求的进程就需要等待该latch的释放。例如redo分配latch、copy latch,归档控制latch等。其基本思路是为了阻止并发。由于指令设置和释放latch是原子的,OS就可以确保只有1个进程获得到latch。因为只有1条指令,所以执行相当快。在latch持有时,会有一个很短暂的时间提供一个清理机制,预防持有进程僵死但仍然持有latch。latch清理将由PMON进程服务完成。
4. latch请求模式
latch请求有2中模式:"愿意等待"或"不等待"("willing-to-wait" or "no wait")。通常,latch在"愿意等待"模式中请求。"愿意等待"模式中的请求将不断循环等待直到获得了该latch。当进程使用"不等待"模式请求latch时,如果该latch不可用,不是等待该latch,而是请求另一个latch。只有所有请求都失败时,服务进程才会等待。
"愿意等待"的latch有:shared pool和library cache的latch。
"不等待"的latch有,redo copy latch。
5. 什么原因导致latch争用
如果需要的latch正在被其他占用,进程将再次尝试spin,如果仍然无法获得,再次spin,该循环请求的最大次数由初始化参数_SPIN_COUNT决定。如果在整个循环完成之后,latch仍然不可获得,进程必须交出CPU时间片,进入sleep,初始化sleep时间为1厘秒,之后每次sleep时间为上次的2倍。
这将引起系统缓慢,因为增加了额外的CPU使用,直到latch有效。进程spinning的后果就是CPU的使用,spinning表示进程在sleep一段间隔后唤醒并检查latch是否有效。
6. 如何确认内部latch的争用
查询相关的数据字典视图:
V$LATCH
V$LATCHHOLDER
V$LATCHNAME
V$LATCH视图中的每行记录了一种类型的latch。视图的列表示了不同类型latch的请求活动。这些类型的请求的区别在于,latch不可用的情况下,进程是否继续请求:
"愿意等待" 该方式请求latch,如果请求失败,请求进程将等待一段时间后再次请求。进程将持续请求直到获得latch。
"不等待" 该方式立即请求latch,如果请求失败,进程不等待,但仍然继续执行。
V$LATCH的关键信息:
-----------------------
GETS "愿意等待"模式下请求获得latch,成功的次数。
MISSES "愿意等待"模式下请求获得latch,首次失败的次数。
SLEEPS "愿意等待"模式下请求获得latch,在首次请求之后等待的次数。
IMMEDIATE_GETS 每个latch立即请求成功的次数
IMMEDIATE_MISSES 每个latch立即请求失败的次数
计算latch命中率
-----------------------
可用下面的公式计算latch命中率:
"愿意等待"模式:命中率=(GETS-MISSES)/GETS
"不等待"模式:命中率=(IMMEDIATE_GETS-IMMEDIATE_MISSES)/IMMEDIATE_GETS
该值最好接近1,如果不是根据latch名称的不同作出相应的调整。
7. 检查latch信息的SQL脚本
/*
** 显示系统级的latch统计信息
*/
column name format A32 truncate heading "LATCH NAME"
column pid heading "HOLDER PID"
select c.name,a.addr,a.gets,a.misses,a.sleeps,
a.immediate_gets,a.immediate_misses,b.pid
from v$latch a, v$latchholder b, v$latchname c
where a.addr = b.laddr(+)
and a.latch# = c.latch#
order by a.latch#;
/*
** 通过latch地址找到latch名称
*/
column name format a64 heading 'Name'
select a.name from v$latchname a, v$latch b
where b.addr = '&addr'
and b.latch#=a.latch#;
/*
** 显示指定名称的latch统计
*/
column name format a32 heading 'LATCH NAME'
column pid heading 'HOLDER PID'
select c.name,a.addr,a.gets,a.misses,a.sleeps,
a.immediate_gets,a.immediate_misses,b.pid
from v$latch a, v$latchholder b, v$latchname c
where a.addr = b.laddr(+) and a.latch# = c.latch#
and c.name like '&latch_name%' order by a.latch#;
8. 列出所有的latch
Oracle不同版本的latch#对应的latch名称可能不一样,通过下面的查询可以查到latch的名称对应。
column name format a40 heading 'LATCH NAME'
select latch#, name from v$latchname;
9. 列出DBA需要关心的latch
-BUFFER CACHE LATCHES:
主要有2中latch保护buffer cache中的数据块。通常在较频繁的IO时会看到这2类latch的争用。可以通过例如调整参数来减少争用。
Cache buffers chains latch:
该latch在buffer cache中的数据块被访问(pinned)时获得。
减少cache buffer chains latch的争用,首先通常是减少逻辑IO的比率,例如优化SQL减少所需的逻辑读。I/O可能频繁发生在热块上(该块被频繁访问)。
参看 NOTE:163424.1 How To Identify a Hot Block Within The Database to correctly identify this issue
Cache buffers LRU chain latch:
cache buffer lru chain latch请求发生在,想把一个新数据块放到buffer cache中的同时buffer正在往磁盘写回,尤其是尝试在buffer cache中扫描LRU(least recently used) chain中包含的脏数据。
减少cache buffer lru chain latch争用,可以增大buffer cache大小,从而降低将新块放入buffer cache的频率。增大buffer cache对于执行全表扫描或其他不使用buffer cache的操作没有效果。增大buffer pool通常可以减少该latch的争用。可以通过隐含参数创建额外的的cache buffer lru chain latch,_DB_BLOCK_LRU_LATCHES,还可以通过增大隐含参数_DB_BLOCK_HASH_BUCKETS,减少cache buffer lru chain latch的负载。
-LIBRARY CACHE
Library cache latch:
Library cache latch用来保护共享池中library cache cache的SQL语句以及对象定义。当增加一个新的语句到library cache中是请求该latch,在解析过程中,Oracle先找寻library cache中是否有匹配语句,如果没有找到,Oracle将解析该语句,获得Library cache latch并插入新的SQL语句。
减少该latch争用,首先应考虑应用中的SQL语句可以重用,尽可能地在应用中使用绑定变量。频繁地解析SQL,不但会造成该latch的miss增多,同时也增加了解析的CPU开销。如果应用使用Library cache使用不当,即使增大了共享池,仍然会有严重的latch争用。
隐含参数 _KGL_LATCH_COUNT控制了Library cache latch的数量。通常该默认值都足够大,除非Library cache latch争用得厉害,需要手动调整该参数,可以适当增大该值。_KGL_LATCH_COUNT该参数默认是在CPU_COUNT值之后最大的质数,该值不能超过66。
Library cache pin latch:
当library cache中的语句被重复执行时请求该latch,高频率地执行相同的SQL会造成miss。少量减少Library cache pin latch的方法,可以使用私有同义词代替公有同义词,使用直接对象名称,如OWNER.TABLE。
-SHARED POOL RELATED LATCHES
Shared pool latch:
Shared pool latch用来保护重要的操作,在共享池中分配和释放内存。如果应用使用文字SQL(不共享),那么可能会由于该latch受限,影响吞吐量。新的SQL语句的解析需要消耗额外的CPU,以及造成library cache和shared pool latch的多次请求和释放。在ORACLE9之前,整个数据库只有一个这类的latch保护library cache分配内存,从ORACLE9起,有多个子latch,减缓了对该资源的争用。
减少shared pool latch的一种方法是尽可能地避免硬解析,只解析1次,执行多次。不使用全文本SQL(没有绑定变量)也可以有效避免Shared pool latch,共享池的大小,以及MTS的使用,对Shared pool latch都有较大影响。
参看 Note 62143.1 检查和解决共享池和Shared pool latch的问题。
Row cache objects latch:
当进程尝试访问cached的数据字典值时会产生该latch。通常该latch不会造成争用,唯一减少该latch争用的方法是增大共享池大小。
10. 调整 _SPIN_COUNT参数(在Oracle7中是 _LATCH_SPIN_COUNT)
_SPIN_COUNT参数控制了进程在未获得latch之前尝试多少次之后进入sleep。基本意思就是在获得CPU时间片时,反复循环尝试获得latch,直到_SPIN_COUNT次数还没获得就进入sleep。如果在一个单CPU的系统中,Oracle尝试再次请求latch,但是CPU被其他进程持有,那么其他进程会短暂地进入sleep,让该进程尝试获得latch。然而,在多处理器系统(SMP)中,可能其他进程在其他CPU的时间片上持有了该latch,等待他们在之后的指令中释放(通常latch的持有是非常短暂的)。
可以通过调整_SPIN_COUNT参数来调整性能。该值高,latch可能更快被获得。可是同时也花费了更多的CPU时间去spinning,因为sleep前的循环多了。增大参数_LATCH_SPIN_COUNT或_SPIN_COUNT,可以减少session的sleep。这些参数决定了在sleep之前需要尝试多少次去获得latch。latch的spinning需要消耗CPU,所以增大这些参数的同时,也增加了CPU的负载。如果系统CPU使用率达到100%,应用的吞吐量和相应时间都不错的情况下,可以考虑减小_SPIN_COUNT,用以降低CPU的使用。调整_SPIN_COUNT参数需要反复实验,通常在CPU资源足够的情况下增大该参数,在CPU资源紧张的时候,减小该参数。
总结latch的sleep和spin的次数,如果存在latch争用,而且CPU资源较多的情况,考虑增大_SPIN_COUNT,如果CPU资源满负荷,考虑减小_SPIN_COUNT。