LATCH FREE等待事件.docx
《LATCH FREE等待事件.docx》由会员分享,可在线阅读,更多相关《LATCH FREE等待事件.docx(32页珍藏版)》请在冰点文库上搜索。
LATCHFREE等待事件
Latchfree等待事件
原文:
oraclewaitinterface—apracticalguidetoperformancediagnostics&tuning
Richmondshee
Kirtikumardeshpande
Kgopalakrishnan
Mcgraw-hill/osborne
2100powellstreet,10thfloor
Emeryville,california94608
U.s.a.
Chapter6:
interpretinglocks-relatedwaitevents-latchfree
文档修订历史:
版本
时间
作者
1.00
2006-4-21
NinGoo
本文只是对原文的一个大概的翻译,建议最好还是阅读原文。
如果你发现有任何问题,欢迎反馈NinGoo@
Latchfree等待事件的三个参数:
p1-latch的地址;p2-latch编号;p3-请求次数。
从oracle10g起,latchfree不再包含所有的latch等待,有些latch等待可能表现为单独的等待事件,这个后面有提到一些这样的等待事件,一般情况下我们还是统称为latchfree等待事件。
在处理latchfree等待事件时,需要注意以下几点:
⏹Latch只是用来保护sga中的内存结构。
对数据库中的对象的保护,使用的lock而不是latch。
Oraclesga中有许多latch,用来保护sga中各种内存结构不会因为并发访问而损坏。
⏹等待latch的是oracle会话。
不同的latch类型会导致会话采取不同的策略。
⏹在oracle9i(包括9i)之前,latchfree等待事件包括了所有的latch等待,但从oracle10g起,latch被分成不同的种类,并且某些latch表现为独立的等待事件。
什么是latch
Latch是一种锁机制。
你应该已经熟悉latch的概念和用法,虽然可能你自己并没有意识到。
在日常的工作和交流中,latch都经常出现,比如你锁门时,需要获得一个latch;或者你坐到车里,系上安全带,你就把自己放在一个latch的保护中了。
在oracle中,latch是一种轻量级的锁。
一般来说,latch由三种内存元素组成:
pid(进程id),内存地址和内存长度。
Latch保证对共享数据结构的排它性访问,以此来保证内存结构的完整性不受到损坏。
在多个会话同时修改或者检视(inspect)sga中同一个内存结构时,必须串行化访问以保证sga中数据结构的完整性。
Latch和lock的异同
Latch和lock有许多不同之处。
下表列出了latch和lock之间的比较结果。
Latch
Lock
目的
只有一个目的:
保证对内存结构的排他性访问(从oracle9i开始,cachebufferschainlatch可以允许只读共享访问)
两个目的:
如果锁模式是兼容的,允许多个进程共享相同的资源;如果锁模型是不兼容的,保证对共享资源的排它性访问。
适用场景
只能应用于sga中的数据结构,保护内存对象。
Latch只影响单次操作,而和事务无关。
保护数据库对象,诸如表,数据块和状态对象等。
由应用程序驱动,控制对数据库中数据和元数据的访问。
Lock是事务性的。
获取方式
两种模式:
willing-to-wait和no-wait
六种模式:
null,rowshare,rowexclusive,share,sharerowexclusive和exclusive
范围
信息都保存在内存中,并且只在本实例可见――latch是实例级别的
信息保存在数据库中,并且该数据库的所有实例都可见――lock是数据库级的
复杂度
使用简单机器指令比如:
test-and-set,compare-and-swap或其他简单的cpu指令实现。
由于cpu指令平台相关,所以latch在不同的平台的具体实现不一样。
轻量级的。
需要上下文切换(contextsiwtch),使用一系列指令实现。
重量级的。
持续事件
非常短暂(通常是微妙级的)
通常在整个事务中都持有。
排队机制
当一个进程获取latch失败,转入睡眠状态时,他的请求不需要按顺序排队(一个例外情况:
latchwaitlistlatch需要排队)。
当一个进程获取lock失败,它的请求会进入一个队列,除非指定nowait。
死锁
Latch的实现方式不会产生死锁(不排队)
Lock的排队机制可能导致死锁。
死锁发生时会产生相应的跟踪文件。
Latch家族
Latch有三种:
父latch,子latch和独立latch。
父latch和独立latch在oracle的内核代码中固化,子latch则在实例启动时创造。
V$latch_parent和v$latch_children视图分别包含父latch和子latch的统计信息。
而v$latch则包含独立latch,父latch及其相应子latch的聚合统计信息。
Latch的获取
进程获取latch有两种模式:
willing-to-wait和no_wait。
No-wait模式只在少数latch中使用。
通过no-wait模式获取latch的统计信息记录在immediate_gets和immediate_misses列中,这些列在v$latch,v$latch_parent,v$latch_children视图中都存在。
一般来说,no-wait模式在第一次获取一些有很多子latch的latch比如redocopy时使用。
如果一个进程第一次获取这些子latch中的任何一个失败,它会立即使用no-wait模式询问下一个。
只有当采用no-wait模式试图获取所有的子latch都失败以后,才会转而采用willing-to-wait模式。
通过willing-to-wait模式获取latch的统计信息存放在gets和misses列中。
每当一个进程用willing-to-wait模式去获取一个latch时,gets都会增加。
如果进程在第一次请求latch时,latch可用,就会直接获得该latch。
在修改任何受到保护的数据结构之前,进程会将一些恢复信息写入到latch恢复区,这样当获得latch的进程发生异常时,pmon进程才能够清理该进程持有的latch。
如果请求latch时,该latch不可用,进程就会在cpu中等待一小段时间(spin)然后重新请求latch。
如果latch一直不可用,该过程(spin一段时间然后重新请求)会一直重复。
重复的次数由隐含参数_spin_count决定,默认值2000。
如果在请求_spin_count次之内获得了latch,就对spin_gets和misses列各加一,否则,进程在v$session_wait中记录latchfree等待事件,然后释放cpu,转入睡眠状态。
睡眠一定时间后,进程被唤醒并重复上面的过程,一直到获得latch。
在成功获得latch后,才会更行sleep列得统计信息。
由于进程只有在获得latch后才会停止对latch得请求,如果某个持有latch的进程发生异常,其他请求该latch的进程该怎么办?
岂不是要一直等待下去?
不会的。
当一个进程请求latch失败一定次数后,它会请求pmon进程查看该latch的持有者,如果持有进程异常,pmon就会清理该进程,释放latch。
每个latch都有一个从0到13的优先级编号。
父latch和独立latch的优先级编号是在oracle内核代码中固定的。
子latch是在实例启动时创建,其优先级编号从其父latch继承。
使用优先级可以避免死锁。
⏹当一个进程请求no-wait模式的latch时,该latch的优先级编号必须和它当前已经持有的latch的优先级编号相同。
⏹当一个进程请求willing-to-wait模式的latch时,该latch的优先级编号必须比它当前已经持有的latch的优先级编号要大。
短等待latch与长等待latch
大多数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,这意味着这些latch被可能长久持有。
如果请求该latch的进程进入睡眠状态,需要其他进程来唤醒,这会产生一个latchwaitposting等待事件,该行为由隐含参数_latch_wait_posting控制。
在oracle8i,只有2个长等待latch,如下面的示例sql(oracle9i和oracle10g有更多长等待latch)所示。
_latch_wait_posting参数从oracle9i起已经废弃,使用latchwaitposting的latch的统计信息被记录在waiters_woken列中。
Selectname,immediate_gets,immediate_misses,
gets,misses,sleeps,waiters_woken
Fromv$latch
Wherewaiters_woken>0;
immediateimmediatewaiters
Namegetsmissesgetsmissessleepswoken
-----------------------------------------------------------------------------------------------------------------------
Sharedpool001846415631241032485
Librarycache8550812415644005404334362151********19
Latch分类
从oracle9iR2开始,latch可以被分成不同的类型,每一类latch都可以有不同的_spin_count值。
在早期版本中,如果改变_spin_count值,会对系统中所有的latch造成影响。
这样可能会增加cpu的负担,而latch分类则正是为解决这个问题而引入的。
例如,如果cachebufferschainslatch的sleep次数很多,而且cpu资源充足,我们就可以将cachebufferchainslatch所在的分类的_spin_count的值调高。
高_spin_count值可以降低sleeps和misses的次数,代价是花费更多cpu时间。
内部视图x$ksllclass(kernelservericelocklatchesclass)包含了latch的所有八种类型的信息。
其中indx列就是latch类型编号。
Selectindx,spin,yield,waittime
fromx$ksllclass;
indxspinyieldwaittime
----------------------------------------
02000001
12000001
22000001
32000001
42000001
52000001
62000001
72000001
8rowsselected.
x$ksllclass中的每行记录都和一个隐藏参数_latch_class_n关联,通过这些隐含参数,你可以改变相应的_spin_count,yield和waittime的值(x$视图不能由用户手动更新)。
例如,latch类型0由参数_latch_class_0控制,latch类型1由参数_latch_class_1控制。
如果你想将cachebufferschainslatch的_spin_count值改成10,000,首先你需要知道latch的编号,通过以下查询可以获得
Selectlatch#,name
Fromv$latchname
Wherename=’cachebufferschains’;
latch#name
-----------------------------------------
97cachebufferschains
然后,你需要修改init.ora中的下面2个参数:
_latch_class_1="10000"
_latch_classes="97:
1"
第一个参数_latch_class_1将类型1的spin_count值改为10,000;
第二个参数_latch_classes将编号为97的latch分配到类型1。
再次查询x$ksllclass,我们可以发现:
Selectindx,spin,yield,waittime
Fromx$ksllclass;
indxspinyieldwaittime
----------------------------------------
02000001
11000001
22000001
32000001
42000001
52000001
62000001
72000001
8rowsselected.
Selecta.kslldnam,b.kslltnum,b.class_ksllt
Fromx$ksllda,x$kslltb
Wherea.kslldadr=b.addr
Andb.class_ksllt>0;
Kslldnamkslltnumclass_ksllt
----------------------------------------------
Processallocation32
Cachebufferschains971
注意:
如果服务器的cpu资源紧张,请不要增加_spin_count的值。
当然,默认值2000是很久以前定下来的值,当时的cpu比现在的cpu要慢得多。
Latchfree等待事件可以告诉我们什么?
如果我们在v$session_wait中发现有latchfree等待事件,就意味着,进程在请求一个willing_to_wait模式的latch,在重试了_spin_count次后还是没有获得latch,然后转入睡眠状态了。
如果latch争用严重,将会由于不断的spin导致cpu资源紧张,从而增加系统响应时间。
V$system_event视图的total_waits列记录了进程获取willing-to-wait模式latch失败的次数。
V$latch的sleeps列记录了进程由于等待某个latch而进入睡眠状态的次数。
由于一个进程在_spin_count次尝试请求latch失败后会转入睡眠状态,total_waits列应该等于sleeps列的值的和,如以下sql所示。
但是,某些时候,total_waits会比sleeps的和要大,这是因为,只有在进程获得latch后才会更新total_waits的值,而不是每次请求latch失败就更新。
Selecta.total_waits,b.sum_of_sleeps
from(selecttotal_waitsfromv$system_eventwhereevent=’latchfree’)a,
(selectsum(sleeps)sum_of_sleepsfromv$latch)b;
total_waitssum_of_sleeps
------------------------
414031680414031680
由于latchfree等待时间一般较短,所以在很少一段时间内,total_waits就可能变得非常大,这并不一定意味着系统有问题。
只有当time_waited的值也非常显著的时候,你才需要关注latchfree等待事件。
Latch失败区域(latchmisslocations)
V$latch_misses视图保存了latch失败在oracle内核代码中的区域信息。
这些信息对于oraclesupport诊断latch等待事件有帮助。
你可以通过以下查询查看位置信息。
Steveadams有篇非常棒的关于这方面的文章.au/newsletter/2001_02.htm
Selectlocation,
parent_name,
wtr_slp_count,
sleep_count,
longhold_count
fromv$latch_misses
wheresleep_count>0
orderbywtr_slp_count,location;
longhold
locationparent_namewtr_slp_countsleep_countcount
------------------------------------------------------------------------
...
Kglupc:
childlibrarycache7879693118696910
kghupr1sharedpool806233154933700
kcbrls:
kslbegincachebufferschains9776543140433550
kqrpfl:
notdirtyrowcacheobjects15606317149991000
kqrpre:
findobjrowcacheobjects20359370209695800
kglhdgn:
child:
librarycache2378255799520930
kcbgtcr:
fastpathcachebufferschains26729974231663370
kglpnc:
childlibrarycache2738535477072040
Oracle10gR1中的latch
在Oracle10g之前,所有的latch等待都显示为latchfree等待事件。
你可以通过latchfree事件的p2参数和v$latch.latch#关联或者通过10046事件来查找某个进程争用的是哪个latch。
而在Oracle10g中,latch被分成许多独立的等待。
下面是oracle10gR1的latch一个列表:
Selectname
fromv$event_name
wherenamelike’latch%’
orderby1;
name
----------------------------------------------------------------
latchactivity
latchfree
latch:
inmemoryundolatch
latch:
kclgcelementparentlatch
latch:
mqltrackinglatch
latch:
cachebufferhandles
latch:
cachebufferschains
latch:
cachebufferslruchain
latch:
checkpointqueuelatch
latch:
enqueuehashchains
latch:
gcsresourcehash
latch:
gesresourcehashlist
latch:
latchwaitlist
latch:
librarycache
latch:
librarycachelock
latch:
librarycachepin
latch:
messages
latch:
objectqueueheaderheap
latch:
objectqueueheaderoperation
latch:
parallelqueryallocbuffer
latch:
redoallocation
latch:
redocopy
latch:
redowriting
latch:
rowcacheobjects
latch:
sessionallocation
latch:
sharedpool
latch:
undoglobaldata
latch:
virtualcircuitqueues
28rowsselected.
Latch产生的原因,诊断以及对策
Latch争用通常意味这某个进程持有latch的时间过长。
如果latch争用明显,系统性能将显著下降。
在高并发的环境中,latch争用经常发生,并且你无法完全消除latch争用。
在v$system_event中总会出现latchfree等待事件。
只有当time_waited相对实例启动以来的总时间比较明显时,你才需要关注latch争用。
当latch在系统范围内的等待时间比较显著时,你可以通过v$latch中的sleeps列来发现争用显著的latch:
Selectname,gets,misses,immediate_gets,immediate_misses,sleeps
fromv$latch
orderbysleeps;
immediateimmediate
namegetsmissesgetsmissessleeps
----------------------------------------------------------------------
enqueuehashchains427709504279001964
sharedpool91066505400002632
rowcacheobjects690598872793840907517
enqueues804433143301670013761
librarycache6944717210334946582719044328
cachebufferschains16910402521166249615326895909127478
...
对不同的latch,其产生的原因以及可采取的对策都有所不