4.1概述
librarycache lock和library cachepin是与library cache相关的等待事件,librarycache锁相关的信息的在x$kgllk和x$kglpn表中。library cache锁既不是enqueue(library cache机制与enqueue很相似,可以称之为KGL enqueue),也不是latch,只是为了保护library cache object对象而存在这种类型的锁,从Oracle10gR2开始,Oracle使用mutex来替换掉一部分library cache锁。习惯上把library cache lock和library cache pin叫做Library cache锁,而实际上这是等待事件,产生等待事件的原因就是因为library cache的lock锁和pin锁。
所有在Library cache中的对象,都由两部分组成,一个句柄、至少一个子堆。句柄中记录的有对象的名字、命名空间、Lock的持有者和等待者、Pin的持有者和等待者、一些标志信息,最重要的,句柄中有堆的地址。在Library cache中寻找对象时,先计算HASH值,在HASH表中找到句柄,再经由句柄,找到对象实际的内存地址。在这个过程中,有两个重要的数据项需要被锁保护起来。一个是对象句柄、另一个就是对象的内存堆。请求对象句柄上的锁就是Library cache lock,请求内存堆上的锁,就是Library cachepin。
4.2library cache lock
librarycache lock锁用于在librarycache中定位一个library cache object(定位一个LCO只需要扫描Library cache handle就可以了,所以申请library cache lock锁只是为了扫描LCH,而如果要修改LCH指向的LCO就需要申请librarycache pin了)。当解析或编译一个SQL或PL/SQL语句时,需要获取相关联的数据库对象(如table,view,procedure,function,package,package body,trigger,index,cluster,synonym等)的library cache lock。当解析或编译完成后释放锁。
Cursors(SQL和PL/SQL),pipe和其它临时对象不使用这种锁(使用mutex)。
Librarycache lock不进行死锁检测(死锁检测开销太大),所有操作都是同步的。(这个原因可能是:enqueue只有在rollback或commit后释放,而library cache 锁一般是执行完就释放,不需要rollback或commit)
librarycache lock等待事件的参数:
P1:句柄地址
要装载的library cache object 的句柄,对应到x$kglob.kglhdadr
P2:锁地址:
使用的锁地址。这与latch或enqueue不同,这是一个StateObject,对应到x$kgllk.kgllkadr
P3:Encoded Mode & Namespace
4.3library cache pin
“librarycache pin”用于管理librarycache的并发。由于需要将一个堆装载到内存,所以需要pin对象。如果客户端(这里的客户端是指shadow server进程)想要修改和检测对象,在请求完library cachelock锁后,需要请求library cache pin锁。PIN锁可以以NULL,SHARE或EXCLUSIVE模式请求,与enqueue锁级别类似。等待事件“library cache pin”表明其它会话以不兼容模式持有PIN锁。
获取library cache pin锁后,就可以获取数据库对象(table,view,procedure,function…)当前在library cache中的缓存。在library cache中,一个对象缓存有2个部分,“handle”和“object”。只有需要处理“object”部分缓存时,才需要library cache pin锁。
同样,library cache pin也没有死锁检测。
4.4为什么需要这两种锁
librarycache lock锁管理着进程的并发性,而librarycache pin锁管理cache的一致性。为了访问一个对象,进程首先要lock句柄(library cache handle),然后pin对象堆(library cache object)。
当请求这library cache锁时,进程会一直请求直到获取。这一般是librarycache锁的争用源头,因为没有NOWAIT这种请求模式。
当在object handle上获得lock锁后,可以防止其它进程访问该对象以及检查该对象是什么类型。因为handle和object这两个部分有关联关系,所以就不需要预防其它进程访问object部分了(因为一定是先访问handle再访问object)。在cache中定位一个对象,请求lock是唯一的方法。进程定位和锁对象是在一个操作里。
如果进程想要测试或修改对象,那就需要获取在该对象上的pin锁(在获取handle的lock锁后)。如果对象不在library cache中,pin对象就会使对象信息和它的堆装载到内存中。在释放pin锁前,这些信息一直在内存中。
编译或解析包、过程、函数或视图时,需要Library cache lock和library cache pin。用于保证在修改过程中没有其它会话使用这些对象。
当SQL语句解析时,会话需要请求library cache lock防止其它会话访问或修改相同的对象。如果这个事件占很多时间,那可能表明共享池太小,或需要定期刷新。否则就表明数据库对象被定期修改。
除了硬解析之外,如果对象想修改SQL中对象的定义或做任意其它修改,都需要请求library cache lock然后是library cache pin。请求pin锁是需要装载相应的数据字典信息到内存,并访问相关代码。
4.5library cache load lock
当对象不在library
cache中时,就无法对library cache handle加锁。这时侯会话需要请求load lock,来把对象装载到内存。load
lock总是以排它模式获取,所以没有其它会话可以在同时装载相同的对象。如果load lock争用,那会话会一直处理等待状态直到load
lock可用。
librarycache pin和load lock可能发生在PL/SQL,视图,类型等的编译和重编译时,因为失效对象可能自动重编译。
4.6诊断
如果发生了library cache lock和library cache pin等待事件,那就表明有会话想要请求library cache的lock锁或pin锁被阻塞了。如果是短时间出现这种事件,一般可能是共享池太小、SQL硬件太高等原因。但如果是长时间或者是大量会话出现这个问题,那可能是在生产时间在编译存储过程或者是异常操作导致,这时侯需要把阻塞者会话找出来杀掉来释放相关的lock锁或pin锁。Oracle在11gR2上还存在一个登录Bug,由于密码错误,并且登录频繁,导致严重的library cache lock等待,具体参考:LIBRARY CACHELOCKS DUE TO INVALID LOGIN ATTEMPTS [ID 1309738.1]。
找出library cache锁的持有者,可以使用如下SQL:
set linesize200 pagesize 999
columnusername format a10
column programformat a18
column machineformat a15
column sql_idformat a14
column eventformat a30
column ownerformat a12
columnobject_name format a25
select s.sid,s.username, s.program, s.machine, s.sql_id, s.event,
s.status, l.kgllktype as type,l.kgllkmod as lmode,
o.kglnaown as owner, o.kglnaobj asobject_name
from dba_kgllock l, x$kglob o, v$session s
wherel.kgllkhdl = '&p1raw'
and l.kgllkreq = 0
and l.kgllkmod > 1
and l.kgllkhdl = o.kglhdadr
and s.SADDR = l.kgllkuse;
这里的p1raw,指v$session中的p1raw。
4.7Event 10049
event10049,这个事件在10gR2以后,专门被用来trace librarycache lock和library cache pin,用10049的难点在于如何确定level。
首先,10049的level可能会有如下一些值:
#defineKGLTRCLCK 0x0010 * trace lock operations*/
#defineKGLTRCPIN 0x0020 * trace pinoperations */
#defineKGLTRCOBF 0x0040 * trace objectfreeing */
#defineKGLTRCINV 0x0080 /* traceinvalidations */
#defineKGLDMPSTK 0x0100 * DUMP CALL STACK WITH TRACE*/
#defineKGLDMPOBJ 0x0200 * DUMP KGL OBJECT WITH TRACE*/
#defineKGLDMPENQ 0x0400 * DUMP KGL ENQUEUE WITH TRACE*/
#defineKGLTRCHSH 0x2000 * DUMP BY HASH VALUE*/
其次,我们是要针对单个sql,所以需要用到这个sql的hashvalue,以便将10049和这个sql联系起来,即我们一定要用到KGLTRCHSH,也就是0x2000;另外我们是要trace library cache lock和library cache pin,所以我们一定要用到KGLTRCLCK和KGLTRCPIN,即0x0010和0x0020;
最后就是我们需要把这个sql的hash value的16进制的后两个byte拿出来,作为10049的level的前缀。
SQL> selecthash_value,to_char(hash_value,'XXXXXXXX') as hex_hash_value, sql_text
from v$sqlarea
where sql_text like 'select * from scott.emp%';
HASH_VALUEHEX_HASH_ SQL_TEXT
------------------- ------------------------------
52404428 31FA0CC select * from scott.emp
KGLTRCHSH| KGLTRCLCK | KGLTRCPIN =0x2000 | 0x0010 | 0x0020 = 0x2030
select* from scott.emp的hashvalue的16进制的后两个byte是0xA0CC。
所以select * from scott.emp的10049 Event的最终level值就是0xA0CC2030,也就是2697732144。
转储过程如下:
硬解析
SQL> altersystem flush shared_pool;
Systemaltered.
SQL>oradebug setmypid
Statementprocessed.
SQL>oradebug event 10049 trace name context forever, level 2697732144
Statementprocessed.
SQL> select* from scott.emp;
......
SQL>oradebug tracefile_name
/opt/oracle/app/oracle/admin/test10/udump/test10_ora_47330.trc
SQL>oradebug event 10049 trace name context off
Statementprocessed.
*** 2016-01-0510:25:29.113
ProcessingOradebug command 'event 10049 trace name context forever, level 2697732144'
KGLTRCLCKkgllkal hd = 0x0xb4cb1940 KGL Lock addr = 0x0xb997d890 mode = N
KGLTRCLCKkglget hd = 0x0xb4cb1940 KGL Lock addr = 0x0xb997d890 mode = N
KGLTRCPINkglpin hd = 0x0xb4cb1940 KGL Pin addr = 0x0xb9763c70 mode = X
KGLTRCPINkglpndl hd = 0x0xb4cb1940 KGL Pin addr = 0x0xb9763c70 mode = X
KGLTRCLCKkgllkal hd = 0x0xbe9e2340 KGL Lock addr = 0x0xb997d160 mode = N
KGLTRCLCKkglget hd = 0x0xbe9e2340 KGL Lock addr = 0x0xb997d160 mode = N
KGLTRCPINkglpin hd = 0x0xbe9e2340 KGL Pin addr = 0x0xb9979c38 mode = X
KGLTRCPINkglpndl hd = 0x0xbe9e2340 KGL Pin addr = 0x0xb9979c38 mode = X
*** 2016-01-0510:25:43.537
KGLTRCLCKkgllkdl hd = 0x0xbe9e2340 KGL Lock addr = 0x0xb997d160 mode = N
KGLTRCLCKkgllkdl2 hd = 0x0xbe9e2340 KGL Lock addr = 0x0xb997d160 mode = 0
KGLTRCLCKkgllkdl hd = 0x0xb4cb1940 KGL Lock addr = 0x0xb997d890 mode = N
KGLTRCLCKkgllkdl2 hd = 0x0xb4cb1940 KGL Lock addr = 0x0xb997d890 mode = 0
SQL> selectkglhdadr,kglhdpar,kglnaown,kglnaobj from x$kglob
2 where kglhdadr like '%B4CB1940%';
KGLHDADR KGLHDPAR KGLNAOWN KGLNAOBJ
-------------------------------- --------- --------------------------
00000000B4CB194000000000B4CB1940 select * fromscott.emp
SQL> selectkglhdadr,kglhdpar,kglnaown,kglnaobj from x$kglob
2 where kglhdadr like '%BE9E2340%';
KGLHDADR KGLHDPAR KGLNAOWN KGLNAOBJ
-------------------------------- ---------- -------------------------
00000000BE9E234000000000B4CB1940 select * fromscott.emp
SQL> selectsql_id, address, child_address, hash_value, last_active_time
2 from v$sql
3 where sql_text like 'select * from scott.emp%';
SQL_ID ADDRESS CHILD_ADDRESS HASH_VALUE LAST_ACTIVE_TIME
----------------------------- ---------------- ---------- ----------------
ggqns3c1jz86c00000000B4CB1940 00000000BE9E2340 52404428 2016-01-05 10:25
软解析
*** 2016-01-0510:42:49.101
ProcessingOradebug command 'event 10049 trace name context forever, level 2697732144'
KGLTRCLCKkgllkal hd = 0x0xb4cb1940 KGL Lock addr = 0x0xb977d4e0 mode = N
KGLTRCLCK kglget hd = 0x0xb4cb1940 KGL Lock addr = 0x0xb977d4e0 mode = N
KGLTRCLCKkgllkal hd = 0x0xbe9e2340 KGL Lock addr = 0x0xb998e080 mode = N
*** 2016-01-0510:43:00.864
KGLTRCLCKkgllkdl hd = 0x0xbe9e2340 KGL Lock addr = 0x0xb998e080 mode = N
KGLTRCLCKkgllkdl2 hd = 0x0xbe9e2340 KGL Lock addr = 0x0xb998e080 mode = 0
KGLTRCLCKkgllkdl hd = 0x0xb4cb1940 KGL Lock addr = 0x0xb977d4e0 mode = N
KGLTRCLCKkgllkdl2 hd = 0x0xb4cb1940 KGL Lock addr = 0x0xb977d4e0 mode = 0
在Libaray cache锁中提到,Oracle 从10g引入的新的串行机制-互斥锁机制MUTEX,用于替换Cursors(SQL和PL/SQL),pipe和其它临时对象使用的library cache锁。从10.2.0.2开始,_kks_use_mutex_pin默认为true,在11gR2中已经取消了该隐含参数。以往使用LIBRARY CACHE PIN的时候,一个锁要维护一组对象(比如一组HASH BUCKET),而MUTEX是嵌入到对象内部的,因此一MUTEX仅仅保护一个特定的对象。这也大大提高了MUTEX并发使用的效率。
从v$mutex_sleep_history视图中查看mutex_type,在10gR2中是:Cursor Parent、Cursor Pin、hash table类型,而11gR2中的类型是:Library Cache、Cursor Pin
相关诊断事件:
cursor: mutex X
cursor: mutex S
cursor: pin S
cursor: pin X
cursor: pin S wait on X
以下两个等待事件是11gR2中新增的:
library cache: mutex X
library cache: mutex S
查看Mutex等待事件参数:
SQL> selectname, parameter1, parameter2, parameter3
from v$event_name n
where n.NAME like '%mutex%';
NAME PARAMETER1 PARAMETER2 PARAMETER3
----------------------------------- ------------ ----------
SecureFilemutex
cursor: mutexX idn value where
cursor: mutexS idn value where
library cache:mutex X idn value where
library cache:mutex S idn value where
其中p1就是mutex的标识符,p2的高8位是持有mutex会话的sid,低8位是一个计数,记录了当前正在以share方式访问该mutex的数量。
SQL> selectp2raw from v$session where event = 'cursor: pin S wait on X';
P2RAW
----------------
0000001F00000000
<SID> <RefCnt>
在systemstate dump的trc文件中,mutex的表现如下:
PROCESS 595:
----------------------------------------
SO: 0x700000600c64ad8, type: 2, owner: 0x0,flag: INIT/-/-/0x00 if: 0x3 c: 0x3
……
Current Wait Stack:
0: waiting for 'cursor: pin S wait on X'
idn=0x77288c8e, value=0x7cb00000000,where=0x500000000
wait_id=20 seq_num=21 snap_id=1
wait times: snap=22.961360 sec,exc=22.961360 sec, total=22.961360 sec
wait times: max=infinite,heur=22.961360 sec
……
所以只需要通过mutex等待事件就可以找到该mutex的持有者会话,但是mutex的持有一般非常短暂,并且mutex争用一般是由其它问题造成,当前最新版本的Oracle这块的bug较少,所以只去诊断mutex没有太大意义。
6.1概述
Oracle数据库使用闩锁(latch)来管理SGA内存的分配和释放。Latch是用于保护SGA区中共享数据结构的一种串行化锁定机制。Latch的实现是与操作系统相关的,比如一个进程是否需要等待一个latch,需要等待多长时间。
Latch是一种能够极快地被获取和释放的系统级锁,它通常用于保护描述buffer cache中block的数据结构、共享池内存结构等。与每个latch相联系的还有一个清除过程,当持有latch的进程成为死进程时,该清除过程就会被调用。Latch还具有相关级别,用于防止死锁,一旦一个进程在某个级别上得到一个latch,它就不可能再获得等同或低于该级别的latch。
6.2SPIN与休眠
spin就是一个进程独占cpu time,直到运行的结束。这个期间其他进程不能获得这个cpu的运行时间。对于单CPU来说没有spin概念。
比如数据缓存中的某个块要被读取,我们会获得这个块的latch,这个过程叫做spin,另外一个进程恰好要修改这个块,他也要spin这个块,此时他必须等待,当前一个进程释放latch后才能spin住,然后修改,如果多个进程同时请求的话,他们之间将出现竞争,没有一个入队机制,一旦前面进程释放所定,后面的进程就蜂拥而上,没有先来后到的概念,并且这一切都发生的非常快,因为Latch的特点是快而短暂。
休眠意味着暂时的放弃CPU,进行上下文切换(context switch),这样CPU要保存当前进程运行时的一些状态信息,比如堆栈,信号量等数据结构,然后引入后续进程的状态信息,处理完后再切换回原来的进程状态,这个过程如果频繁的发生在一个高事务,高并发进程的处理系统里面,将是个很昂贵的资源消耗,所以Oracle选择了spin,让进程继续占有CPU,运行一些空指令,之后继续请求,继续spin,直到达到_spin_count值,这时会放弃CPU,进行短暂的休眠,再继续刚才的动作。初始状态下,一个进程会睡眠0.01秒。然后醒过来,并再次尝试获得latch。 进程一旦进入睡眠状态,则会抛出一个对应的等待事件,并记录在视图v$session_wait里,说明当前该进程正在等待的latch的类型等信息。
6.3Latch的种类
愿意等待(Willing-To-Wait)
大部分的latch都属于这种类型(Willing-To-Wait)。这种类型的latch都是通过Test-And-Set的方式来实现的。
任何时候,只有一个进程可以访问内存中的某一个数据块,如果进程因为别的进程正占用块而无法获得Latch时,他会对CPU进行一次spin(旋转),时间非常的短暂,spin过后继续获取,不成功仍然spin,直到spin次数到达阀值限制(这个由隐含参数_spin_count指定),此时进程会停止spin,进行短期的休眠,休眠过后会继续刚才的动作,直到获取块上的Latch为止。进程休眠的时间也是存在算法的,他会随着spin次数而递增,以厘秒为单位,如1,1,2,2,4,4,8,8,。。。休眠的阀值限制由隐含参数_max_exponential_sleep控制,默认是2秒,如果当前进程已经占用了别的Latch,则他的休眠时间不会太长(过长会引起别的进程的Latch等待),此时的休眠最大时间有隐含参数_max_sleep_holding_latch决定,默认是4厘秒。这种时间限制的休眠又称为短期等待。
另外一种情况是长期等待锁存器(Latch Wait Posting),此时等待进程请求Latch不成功,进入休眠,他会向锁存器等待链表(Latch Wait List)压入一条信号,表示获取Latch的请求,当占用进程释放Latch时会检查Latch Wait List,向请求的进程传递一个信号,激活休眠的进程。Latch Wait List是在SGA区维护的一个进程列表,他也需要Latch来保证其正常运行,默认情况下share pool latch和library cache latch是采用这个机制。
如果将隐含参数_latch_wait_posting设置为2,则所有Latch都采用这种等待方式,使用这种方式能够比较精确的唤醒某个等待的进程,但维护LatchWait List需要系统资源,并且对Latch Wait List上Latch的竞争也可能出现瓶颈。
如果一个进程请求,旋转,休眠Latch用了很长时间,他会通知PMON进程,查看Latch的占用进程是否已经意外终止或死亡,如果是,则PMON会清除释放占用的Latch资源。
不等待(No-Wait)
这种类型的latch比较少,对于这种类型的latch来说,都会有很多个可用的latch。当一个进程请求其中的一个latch时,会以no-wait模式开始请求。如果所请求的latch不可用,则进程不会等待,而是立刻请求另外一个latch。只有当所有的latch都不能获得时,才会进入等待。
6.4Latch和Lock的区别
Latch是对内存数据结构提供互斥访问的一种机制,而Lock是以不同的模式来获取共享资源对象,各个模式间存在着兼容或排斥,从这点看出,Latch 的访问,包括查询也是互斥的,任何时候,只能有一个进程能pin住内存的某一块,幸好这个过程是相当的短暂,否则系统性能将没的保障
Latch只作用于内存中,他只能被当前实例访问,而Lock作用于数据库对象,在RAC体系中实例间允许Lock检测与访问
Latch是瞬间的占用,释放,Lock的释放需要等到事务正确的结束,他占用的时间长短由事务大小决定
Latch是非入队的,而Lock是入队的
Latch不存在死锁,而Lock中存在。
6.5Latch的cleanup
在latch的使用过程中,可能会出现一些异常,而导致有些latch被异常占有得不到释放,这样就会有问题了,别的进程过来请求不到。出现这样的异常pmon进程会跟进处理,对于其处理的流程来说,最重要的莫过于将没有提交的事务回滚,那么就需要latch支持恢复,那么latch在开始操作前会先写一些信息去latch的恢复区。Pmon 3秒钟会自动运行一下,但是这也是很长的一段时间了,所以在进程在请求一个latch失败多次之后,会post pmon进程去check一下占有这个latch的process是不是正常。
6.6Latch的level
Latch的级别分为0~14,共15个级别,0级最低,14最高。如果两个latch之间有联系,只能请求level更高的latch。原因如下:
如果a进程占有一个level 为5的latch,它去请求一个level为3的latch,而进程b,占有这个level为3的latch,又去请求那个level 为5的latch,这样会有什么问题呢?因为它是可以去spin的,又是可以去sleep的,sleep之后还是继续重复,那就永远没有完没有了了。所以呢,level的request是有level顺序的,不能随便的请求,只能由低级的latch去请求高级的latch。
如果进程a一定要申请进程b的latch的话,只能放弃原有latch level5为的latch重新对b进程的latch进行申请。
6.7 Latch资源争用
如果latch资源被争用,通常都会表现为CPU资源使用过高,其主要原因有以下几点:
SQL语句 如果没有使用绑定变量,很容易造成频繁读写shared pool里的内存块,如果存在大量的SQL被反复分析,就会造成很大的Latch争用和长时间的等待, 从而导致与解析SQL相关的共享池中的Latch争用 。与 shared pool共享池相关的latch有Library Cache Latch 和Shared Pool Latch。
访问频率非常高的数据块被称为热快(Hot Block),当很多用户一起去访问某几个数据块时,就会导致 数据缓冲池Latch争用,最常见的latch争用有 :buffer busywaits和cache buffer chain。
Cachebuffer chian:
当一个会话需要去访问一个内存块时,它首先要去一个像链表一样的结构中去搜索这个数据块是否在内存中,当会话访问这个链表的时候需要获得一个Latch, 如果获取失败,将会产生Latch cache buffer chain 等待,导致这个等待的原因是访问相同的数据块的会话太多或者这个列表太长(如果读到内存中的数据太多,需要管理数据块的hash列表就会很长,这样会话扫 描列表的时间就会增加,持有chache bufferchain latch的时间就会变长,其他会话获得这个Latch的机会就会降低,等待就会增加)。
Bufferbusy waits:
当一个会话需要访问一个数据块,而这个数据块正在被另一个用户从磁盘读取到内存中或者这个数据块正在被另一个会话修改时,当前的会话就需要等待,就会产生一个buffer busy waits等待。
产生这些Latch争用的直接原因是太多的会话去访问相同的数据块导致热快问题,造成热快的原因可能是数据库设置导致或者重复执行的SQL
频繁访问一些相同的数据块导致。热块产生的原因不尽相同,按照数据块的类型,可以分成以下几种热块类型,不同热块类型处理的方式都是不同的:表数据块、索引数据块、索引根数据块和文件头数据块。
另外也有一些latch等待与bug有关,应当关注Metalink相关bug的公布及补丁的发布。
为何latch的争用会引起CPU使用率较高呢?
其实很容易理解,比如进程A持有latch,此时进程B也需要持有相关latch,但是没有获得,这时候进程B就需要进行spin,如果类似进程B的进程较多的话,对CPU进行spin的进程就会较多,表现也就是CPU利用率非常高,但是吞吐量却很低,典型的“出工不出活”。
中国OCM之家
专注数据 共现梦想
QQ群:554334183