Latch free等待事件二(转)

Latch产生的原因,诊断以及对策

Latch争用通常意味这某个进程持有latch的时间过长。如果latch争用明显,系统性能将显著下降。在高并发的环境中,latch争用经常发生,并且你无法完全消除latch争用。在v$system_event中总会出现latch free等待事件。只有当time_waited相对实例启动以来的总时间比较明显时,你才需要关注latch争用。当latch在系统范围内的等待时间比较显著时,你可以通过v$latch中的sleeps列来发现争用显著的latch

对不同的latch,其产生的原因以及可采取的对策都有所不同。详细的说明所有的latch可以写成一本书了。这里我们只选择最常见的五个latch加以说明:shared pool, library cache, cache buffers chains, cache buffers lru chainrow cache objects

Shared poollibrary cache latch

Oracle的共享池由不同的结构组成。主要包括:数据字典缓存,sql区和库缓存。通过v$sgastat你可以查看其他一些结构。Shared pool latch主要用来保护共享池的内存结构,当分配或者释放共享池内存时需要先获得该latch。例如,为一个新的sql语句或pl/sql过程、函数、包,触发器等分配空间(硬解析)时,或者为换出、清除某些内存块,以便为新的对象腾出足够的空间时,都需要获取shared pool latch

oracle9i之前,共享池内存结构由一个独立shared pool latch保护,从9i开始,则有最多7个子latch可以用于共享池的保护。这也是为什么oracle9i可以将共享池分成多个子共享池的原因(服务器至少需要4cpu,并且shared_pool_size大于250m才能使用多个子共享池的特性)。子共享池的个数可以通过隐含参数_kghdsidx_count手动调节,该参数同时会指定合适的shared poollatch的个数。如果你手动增加子共享池的个数,你应该同时增加shared_pool_size的值,因为每个子共享池都有自己的结构,lru列表和shared pool latch。否则,实例启动时可能会遇到以下错误:

Ora-04031: unable to allocate 32 bytes of shared memory ("shared pool","unknown object","sga heap(5,0)","fixed allocation callback").

下面的统计信息是从一个16cpushared_pool_size256moracle9i数据库中读取的。由_kghdsidx_count参数可知共享池被分成2个子池,通过x$kghlu(kernel generic heap lru)可以知道lru列表也有2个。v$latch_children视图显示了7个子latch中的2个已经被使用。

库缓存中主要保存游标,sql语句,执行计划,分析树等。这些结构由library cache latch保护。当oracle进程修改、检查、销连接(pinning)、锁定、装载,或者执行库缓存中的结构时,都需要先获得library cache latch。通过查询v$latch_children可以得知当前实例中的library cachelatch的个数。通常应该为大于cpu个数的最小质数,该值由隐含参数_kgl_latch_count控制。从oracle9i开始,v$sqlarea视图增加了一个child_latch列,用来指示游标在各个library cache latch是如何分布的。

Shared pool library cache latch争用原因一 ―― 分析

Shared poollibrary cache latch争用通常是由于硬分析引起。硬分析需要分配新的游标,或者将已经换出的游标重新执行。硬分析过多说明sql语句没有充分绑定变量。硬分析是代价十分昂贵的操作,在分析期间需要一直持有ibrary cache latch

n 通过下列查询可以发现系统中是否存在大量硬分析。软分析数则可以用总分析数减去硬分析数获得

 
 
 
 

n 查看当前会话是否有大量硬分析

oracle10g中,通过v$sess_time_model视图中对硬分析和失败分析的时间统计信息,可以知道硬分析的来源。下面的例子显示了某个会话的v$sess_time_model信息。

 
 

上面的分析统计信息可以按照下面的方法分组:

1. parse time elapsed

2. hard parse elapsed time

3. hard parse (sharing criteria) elapsed time

4. hard parse (bind mismatch) elapsed time

2. failed parse elapsed time

3. failed parse (out of shared memory) elapsed time

n 确定系统中的常量sql语句(literal sql),它们往往都是可以使用并且应该使用绑定变量的。下面通过查询v$sqlarea视图,列出超过4个执行实例的sql语句的前40个字符,这里假设你的系统中前40个字符相同的语句被认为是没有使用绑定变量的常量sql。很明显,如果使用更长的字符串或者更多的执行实例作为过滤条件,得到的结果sql语句会少很多。然后你可以根据得到的结果,建议程序开发人员对这些常量sql语句尽量使用绑定变量。

oracle9i中,也可以通过如下的语句查询v$sql视图中使用了相同的执行计划的sql语句,这些语句也可能是常量sql语句。

Select plan_hash_value, hash_value
from v$sql
order by 1,2;

如果你的系统中存在大量的常量sql语句,当你将它们改为充分使用绑定变量后,对shared pool latchlibrary cache latch的争用将会显著减少。更改sql语句,使用绑定变量,这通常需要修改应用程序。另外一个不需要改动应用的方法是,修改初始化参数cursor_sharing,将其值改为force(注:原文如此。一般情况下请尽量使用similar而不是force),这个参数允许系统对一些只有常量值不一样的sql语句共享游标,以减少latch争用、内存占用和硬分析。

注意:在oracle8i早期版本中,使用cursor_sharing可能导致bug。在使用了物化视图的环境中,请慎用该参数,否则可能导致长久的library cache pin等待。另外,使用cursor_sharing = force可能导致优化器生成错误的执行计划。这或多或少的会对系统性能造成影响。从oracle9i起,优化器可以通过窥视pga中的信息来进行变量绑定,以此生成合适的执行计划,该特性可以通过隐含参数_optim_peek_user_binds来开启,并且该特性只对那些需要硬分析的sql语句有效,这意味着会基于绑定变量的第一个值来生成执行计划。

当一个新的sql语句到达时,oracle首先在库缓存中检查是否已经有相同的语句存在。如果已经存在,则可以花费较小的代价执行该语句,这就是所谓的软分析。硬分析通常意味着较坏的性能,而软分析过多也不是什么好事。在软分析期间,需要持有library cache latch,并且oracle依然需要对语句进行语法和语义检查,除非该语句已经在会话的游标缓存中。你可以通过设置参数session_cached_cursors来减少library cache latch的持有时间(具体信息请查看oracle metalin,编号#30804.1 #62143.1)。但是,减少软分析的最佳方法还是优化应用程序。最好是分析一次,执行多次(很像java的宣传口号),而不要分析一次,执行一次。你可以通过v$sqlareaparse_calls列来查找分析过多的sql语句。

Shared pool latch争用原因二 ―― 过大的共享池

oracle9i起,由于引入了多个子共享池的特性,过大的共享池不再是一种坏事。在9i之前,过大的共享池通常会引起shared pool latch争用。共享池中可用内存分成不同的内存块(chunk),不同大小范围的块由不同的可用列表(freelist)来管理。在共享池中分配空间时,需要扫描可用列表,扫描期间,需要持有shared pool latch。过大的共享池会使得可用列表过长,从而使得shared pool latch的持有时间变长。在高并发环境中,latch持有时间过长就可能造成latch争用(表现为较高的sleepsmisses值),尤其是大量使用常量sql的系统,对这样的系统,不要一味想着加大共享池,更重要的是想一想你为什么会需要保存这么多不能共享的语句到共享池中。

通过alter session set events ’immediate trace name heapdump level 2’可以转存共享池信息,从中可以看到共享池的可用列表信息。在生成的跟踪文件中查找bucket,你可以发现内存块(chunk)分配到不同的bucket上。另外,你也可以通过下面的方法生成一个查询来列出共享池的可用内存管理信息。该查询只要生成一次,就可以在生成该跟踪文件的数据库中重复使用,但不要在其他不同版本的数据库中使用,否则可能得到错误的结果。该查询在oracle10gR1中不可用,因为它限制了case分支数不能超过128(具体信息参考oracle metalink 编号#131557.1bug#3503496),或者你可以通过使用decodesign函数来完成该功能。



如果你发现数据库的共享池可用列表过长,并且系统中使用了常量sql,你或许应该考虑减少shared_pool_size。这会降低系统中对shared pool latch的争用。但要注意共享池不能太小,否则可能导致ora-04031错误。另外,通过使用dbms_shared_pool.keep将常用对象钉在内存中也是个好主意,v$db_object_cache中保存了钉在内存中的对象的信息。

Library cache latch争用原因三 ―― 语句版本数过多

对于字符完全一致但是由于引用不同的对象而不能共享的sql语句,oracle使用多个子游标来指向该语句的不同版本。例如,系统中有三个名叫customer的表,但是属于不同的模式。则对于语句select * from customer,不同的模式执行该语句,语句字符上完全一样,其hash值完全一样,但是该语句无法共享,因为它引用的对象不同。所以会生成该语句的不同子版本。当一个sql语句有多个子版本时,oracle需要比较该语句的所有存在的子版本,在此期间需要持有library cache latch,这样可能导致library cache latch争用。解决这种情况也很简单,在系统中,尽量不要使用相同的对象名。下面的查询列出了v$sqlarea中子版本超过20的所有sql语句:

Select version_count, sql_text
from v$sqlarea
where version_count > 20
order by version_count, hash_value;

注意:在oracle8i中,语句的版本过多,可能导致和sql执行监控相关的bug(参考oracle metalink 编号#62143.1)。这个bug会导致sql无法共享。可以通过将隐含参数_sqlexec_progression_cost设置为0来禁用sql执行监控特性,该参数同时会禁止v$session_longops中的数据。

Oracle提供了视图v$sql_shared_cursor,其中可以看到为什么无法共享一个已经存在子游标。每个列都限制了游标无法共享的一个原因。

-- 大写的列是oracle9i起才有列.
-- oracle 10g R1
还有其他8个列
select a.*, b.hash_value, b.sql_text
from v$sql_shared_cursor a, v$sqltext b, x$kglcursor c
where a.unbound_cursor || a.sql_type_mismatch ||
a.optimizer_mismatch || a.outline_mismatch ||
a.stats_row_mismatch || a.literal_mismatch ||
a.sec_depth_mismatch || a.explain_plan_cursor ||
a.buffered_dml_mismatch || a.pdml_env_mismatch ||
a.inst_drtld_mismatch || a.slave_qc_mismatch ||
a.typecheck_mismatch || a.auth_check_mismatch ||
a.bind_mismatch || a.describe_mismatch ||
a.language_mismatch || a.translation_mismatch ||
a.row_level_sec_mismatch || a.insuff_privs ||
a.insuff_privs_rem || a.remote_trans_mismatch ||
a.logminer_session_mismatch || a.incomp_ltrl_mismatch ||
a.overlap_time_mismatch || a.sql_redirect_mismatch ||
a.mv_query_gen_mismatch || a.user_bind_peek_mismatch ||
a.typchk_dep_mismatch || a.no_trigger_mismatch ||
a.flashback_cursor <> ’nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn’
and a.address = c.kglhdadr
and b.hash_value = c.kglnahsh
order by b.hash_value, b.piece;

Cache buffers chains latch

当一个数据块读入到sga中时,该块的缓冲区头(buffer header)会放置在一个hash bucket的链表(hash chain)中。该内存结构由一系列cache buffers chainslatch保护(又名hash latch或者cbc latch)。下图描述了hash latchhash bucketbuffer headerhash chain的关系

图一. Oracle8i以上的数据缓冲区示意图

一个进程要添加,删除,查找,检视,读取或者修改hash chain上的块,必须先获得cache buffers chains latch,以保证对该chain的排他访问,为保证完整性,必须牺牲并发性。

注:从oracle9i开始,对cache buffer chains latch可用只读共享访问,这可以减少部分争用,但并不能完全消除争用。

一个特定的块头具体分配到哪个hash bucket,是通过dba(data block address)和隐含参数_db_block_hash_buckets实现的。例如,hash bucket = mod(dba, _db_block_hash_buckets)。通过查询v$bhx$bh视图可以发现缓冲区头的争用,也可以通过以下语句转存缓冲区头的信息:

Alter system set events ’immediate trace name buffers level 1’;

oracle8.0之前,每一个hash bucket都有一个cache buffers chains latchhash latch),并且hash bucket都只有一个hash chain链表。换句话说,hash latch,hash backethash chain之间是1:1:1的关系。默认的hash bucket个数为大于db_block_buffers / 4的最小质数,通过隐含参数_db_block_hash_buckets可以修改该值。例如,假如db_block_buffers = 50000,则该实例中有12501hash latch,有12501hash bucket,有12501hash chain

oracle8i开始,oraclehash latchhash bucket之前的关系改成了 1:m,但hash buckethash chain之间还是1:1,也就是一个hash latch可以同时保护多个hash chain链表。这样,可以显著的减少系统中hash latch的个数。Hash latch的默认个数还是和db_block_buffers的值相关。当数据缓冲区小于1G时,一般都是1024个。通过隐含参数_db_blocks_hash_latches可以修改该值。下面的语句查询实例中的hash latch数目:

Hash bucket的默认个数等于2 * db_block_buffers,可以通过隐含参数_db_block_hash_buckets修改。这样,假如db_block_buffers=50000,则系统中有100000hash bucket100000hash chain,但是只有1024hash latch(假设块大小为8k)。由此可以看出,oracle8ioracle8.0中,hash latch数目发生了显著的变化。许多DBA认为,由于hash latch数目显著减少,可能会导致latch争用的增加。但是,oracle解释说,通过以8为因子增加hash chain链表的个数,单个链表会比以前变得更短,这样每次cache buffer chains latch的持有时间也变得更短,以此来补偿latch个数减少带来的latch争用。但不要依赖这个机制,你还是会看到很多latch争用。

Oracle10g使用了不同的算法来决定系统中hash bucket的默认个数。一开始好像等于db_cache_size / 4。但后来的测试证明,在某些不同的db_cache_size值范围内,例如当db_cache_size132m260m之间时,hash bucket的个数是一个常数。下面的表中列出了从oracle8ioracle10g,不同的数据缓冲大小时的hash bucket的默认个数,其中数据块大小都是8k


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/756652/viewspace-242307/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/756652/viewspace-242307/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值