ITPub博客

首页 > Linux操作系统 > Linux操作系统 > Latch free等待事件二

Latch free等待事件二

原创 Linux操作系统 作者:chinagjj2008 时间:2009-04-14 08:58:31 0 删除 编辑

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

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

Select name, gets, misses, immediate_gets, immediate_misses, sleeps 
from v$latch
order by sleeps;
immediate immediate
name gets misses gets misses sleeps
-------------------- ---------- ---------- ----------- --------- ----------
enqueue hash chains 42770950 4279 0 0 1964
shared pool 9106650 5400 0 0 2632
row cache objects 69059887 27938 409 0 7517
enqueues 80443314 330167 0 0 13761
library cache 69447172 103349 465827 190 44328
cache buffers chains 1691040252 1166249 61532689 5909 127478
. . .

对不同的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个已经被使用。

Select a.ksppinm, b.ksppstvl 
from x$ksppi a, x$ksppsv b
where a.indx = b.indx
and a.ksppinm = ’_kghdsidx_count’;

ksppinm ksppstvl
------------------ ----------
_kghdsidx_count 2

select addr, kghluidx, kghlufsh, kghluops, kghlurcr, kghlutrn, kghlumxa
from x$kghlu;

addr kghluidx kghlufsh kghluops kghlurcr kghlutrn kghlumxa
---------------- -------- ---------- ---------- -------- -------- ----------
80000001001581b8 2 41588416 496096025 14820 17463 2147483647
8000000100157e18 1 46837096 3690967191 11661 19930 2147483647

select addr, name, gets, misses, waiters_woken
from v$latch_children
where name = ‘shared pool’;

addr name gets misses waiters_woken
---------------- ------------- ----------- ---------- -------------
c00000004c5b06b0 shared pool 0 0 0c00000004c5b0590 shared pool 0 0 0c00000004c5b0470 shared pool 0 0 0c00000004c5b0350 shared pool 0 0 0c00000004c5b0230 shared pool 0 0 0c00000004c5b0110 shared pool 1385021389 90748637 12734879c00000004c5afff0 shared pool 2138031345 413319249 44738488

库缓存中主要保存游标,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是如何分布的。

Select count(*)
from v$latch_children
where name = ‘library cache’;

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

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

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

Select a.*, sysdate-b.startup_time days_old 
from v$sysstat a, v$instance b
where a.name like ‘parse%’;

statistic# name class value days_old
---------- ------------------------- ----- ---------- ----------
230 parse time cpu 64 33371587 4.6433912
231 parse time elapsed 64 63185919 4.6433912
232 parse count (total) 64 2137380227 4.6433912
233 parse count (hard) 64 27006791 4.6433912
234 parse count (failures) 64 58945 4.6433912
备注:分析失败可能是由于“ora-00942: table or view does not exist”错误或者共享内存不足。

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

Select a.sid, c.username, b.name, a.value, 
round((sysdate - c.logon_time)*24) hours_connected
from v$sesstat a, v$statname b, v$session c
where c.sid = a.sid
and a.statistic# = b.statistic#
and a.value > 0
and b.name = ‘parse count (hard)’
order by a.value;

sid username name value hours_connected
---- ---------- ------------------ ---------- ---------------
510 sys parse count (hard) 12 4
413 pmappc parse count (hard) 317 51
37 pmhcmc parse count (hard) 27680 111
257 pmappc parse count (hard) 64652 13
432 pmappc parse count (hard) 105505 13

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

Select * 
From   v$sess_time_model 
Where  sid = (select max(sid) from v$mystat);
 sid    stat_id stat_name                                             value
---- ---------- ------------------------------------------------ ----------
 148 3649082374 db time                                            11141191
 148 2748282437 db cpu                                              9530592
 148 4157170894 background elapsed time                                   0
 148 2451517896 background cpu time                                       0
 148 4127043053 sequence load elapsed time                                0
  148 1431595225 parse time elapsed                                  3868898
  148  372226525 hard parse elapsed time                             3484672
 148 2821698184 sql execute elapsed time                            9455020
 148 1990024365 connection management call elapsed time                6726
  148 1824284809 failed parse elapsed time                                 0
  148 4125607023 failed parse (out of shared memory) elapsed time          0
  148 3138706091 hard parse (sharing criteria) elapsed time            11552
  148  268357648 hard parse (bind mismatch) elapsed time                4440
 148 2643905994 pl/sql execution elapsed time                         70350
 148  290749718 inbound pl/sql rpc elapsed time                           0
 148 1311180441 pl/sql compilation elapsed time                      268477
 148  751169994 java execution elapsed time                               0

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

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语句尽量使用绑定变量。

Select hash_value, substr(sql_text,1,80)
from v$sqlarea
where substr(sql_text,1,40) in (select substr(sql_text,1,40)
from v$sqlarea
having count(*) > 4
group by substr(sql_text,1,40))
order by sql_text;

hash_value substr(sql_text,1,80)
---------- -----------------------------------------------------------------
2915282817 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
2923401936 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
303952184 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
416786153 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
2112631233 select revenue.customer_id, revenue.orig_sys, revenue.service_typ
3373328808 select region_id from person_to_chair where chair_id = 988947
407884945 select region_id from person_to_chair where chair_id = 990165
3022536167 select region_id from person_to_chair where chair_id = 990166
3204873278 select region_id from person_to_chair where chair_id = 990167
643778054 select region_id from person_to_chair where chair_id = 990168
2601269433 select region_id from person_to_chair where chair_id = 990169
3453662597 select region_id from person_to_chair where chair_id = 991393
3621328440 update plan_storage set last_month_plan_id = 780093, pay_code
2852661466 update plan_storage set last_month_plan_id = 780093, pay_code
380292598 update plan_storage set last_month_plan_id = 780093, pay_code
2202959352 update plan_storage set last_month_plan_id = 780093, pay_code
. . .

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> oradebug setmypid
statement processed.
Sql> oradebug dump heapdump 2
statement processed.
Sql> oradebug tracefile_name
/u01/admin/webmon/udump/orcl_ora_17550.trc
sql> exit
(注:上面是使用oradebug获得转存文件,也可以使用alter session set events ’immediate trace name heapdump level 2’
)
$ grep bucket /u01/admin/webmon/udump/orcl_ora_17550.trc > tmp.lst
$ sed ’s/size=/ksmchsiz>=/’ tmp.lst > tmp2.lst
$ sed ’s/ bucket //’ tmp2.lst | sort –nr > tmp.lst

# 通过shell脚本生成查询
echo ’select ksmchidx, (case’
cat tmp.lst | while read line
do
echo $line | awk ’{print "when " $2 " then " $1}’
done
echo ’end) bucket#,’
echo ’ count(*) free_chunks,’
echo ’ sum(ksmchsiz) free_space,’
echo ’ trunc(avg(ksmchsiz)) avg_chunk_size’
echo ’from x$ksmsp’
echo "where ksmchcls = ’free’"
echo ’group by ksmchidx, (case’;
cat tmp.lst | while read line
do
echo $line | awk ’{print "when " $2 " then " $1’}
done
echo ’end);’

如果你发现数据库的共享池可用列表过长,并且系统中使用了常量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数目:

Select count(distinct(hladdr)) 
from x$bh;

Count(distinct(hladdr))
-----------------------
1024

Select count(*) 
from v$latch_children
where name = ’cache buffers chains’;

  count(*)
----------
1024

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/464838/viewspace-588908/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2008-01-14

  • 博文量
    32
  • 访问量
    60163