作者介绍
梁铭图,新炬网络首席架构师,十多年数据库运维、数据库设计、数据治理以及系统规划建设经验,拥有Oracle OCM、Togaf企业架构师(鉴定级)、IBM CATE等认证,曾获dbaplus年度MVP以及华为云MVP等荣誉,并参与数据资产管理国家标准的编写工作。在数据库运维管理和架构设计、运维体系规划、数据资产管理方面有深入研究。
Oracle数据库服务器由一个数据库和至少一个数据库实例组成。数据库(database)是一组存储数据的文件,而数据库实例则是一组管理数据库文件的内存结构及相应的进程所组成,如常见的pmon,smon,dbwr,ckpt等进程,以及SGA、PGA等内存结构。这些进程和内存结构,共同协作肩负起Oracle数据库所有用户的数据查询以及存取操作。
其中上图红圈标注的library cache 内存结构是ORACLE极为核心也是极为复杂的一部分,日常运维遇到与library cache 相关的性能问题或故障也屡见不鲜,理解 library cache 内存结构必不可少,这次以library cache lock、pin等待事件为题,结合相关案例对 library cache 内存结构做一次深入剖析。
一、Library Cache内存结构了解
library cache的作用官方给出的解释是:library cache是一个用来存放可执行的SQL与PL/SQL代码的共享内存结构。内存中缓存了包括共享SQL和PL/SQL区域以及控制这些结构信息,如相关锁以及library cache的句柄。
执行一个SQL语句时,数据库会尝试重用之前执行过的代码。如果一个SQL语句经过解释(parse)后表明它已经存在于library cache并且可共享,数据库就重用这个执行代码,这个过程一般叫做软解释(soft parse)和一次库缓冲命中(library cache hit)。反之,数据库就必须重新建立一个关于这个SQL的可执行版本的代码,这被称为一次硬解释(hard parse)和一次库缓冲未命中(library cache miss)。
所以library cache中主要存放SQL的shared cursor和PLSQL对象的信息,还包括这些对象所依赖的表、索引等对象的信息。我们可以通过相关视图一探究竟:
以上这图可看出library cache大体是由 hash bucket以及对应的library object handle双向链组建成的框架。
放大一点里面的构造,从以上图又可知道,一个latch管理多个hash bucket,然后一个hash bucket里的library object handle上的访问存在三种队列信息,包括owners\converters\waiters队列,它们存放着持有该handle资源、等待该handle资源的队列信息,其中converers理解为由等待变换成持有这个过程的队列信息。
library cache基本框架知道后,当新增一个对象插入到library cache双向链中,从以上图就可知道双向链的结构变化过程,原理就是通过0x开头的一串数字唯一标识handle本身的位置,另外handle还存放它前、后handle的指针标识,这样就实现双向链的无缝衔接。
如果继续放大handle里的内容就如上图所示,我们知道handle有它0x开头的一串数字唯一标识,这样就可以唯一指向handle的dependency table(依赖对象)、child table(子对象)、authorization table(对象授权信息)、type(对象类型)、status flags(状态标识)、Data Blocks(指向数据存放的data heap), 其实上图是前辈们根据Library cache里存放的内容所绘制而成以方便理解它里面的结构,如果想更真实、直观地理解上图可通过dump出library cache里的内容以作梳理。
执行一条测试SQL。
dump library cache.
测试SQL在library cache里存放的信息。
去掉一些不关心的信息,进一步看Dependencies的内容,从以下部分更清晰的知道SQL有两个依赖对象的内容信息:
另外从dump信息也可知道SQL执行的效率信息:CPU TIME\ Elapsed time等。
Library cache解决三个问题:
1)快速定位的问题:Library cache中对象众多,Oracle如何管理这些对象,以便服务进程可以迅速找到他们需要的信息。比如某个服务进程需要迅速定位某个SQL是否存在于Library cache中。
2)关系依赖的问题:Library cache中的对象存在复杂的依赖关系,当某个object失效时,可以迅速将依赖其的对象也置为失效状态。比如某个表发生了结构变化,依赖其的SQL语句需要重新解析。
3)并发控制的问题:Library cache中必须有一个并发控制的机构,比如锁机制,来管理大量共享对象的并发访问和修改的问题,比如某个SQL在重新编译的同时,其所依赖的对象不能被修改。
二、Library Cache相关等待事件
在日常运维中,与library cache相关的等待事件主要是以下两个:
library cache lock是维护library object handle上并发访问;
library cache pin是维护library object handle下对应heaps的并发访问的。
我们可以用以下的实验来模拟这个过程:
SQL>
create or replace procedure P_TEST
is
begin
dbms_lock.sleep(10000);
end;
/
Procedure created.
SQL>
SELECT kglnaown "Owner", kglnaobj "Object",kglhdadr handle_address
FROM x$kglob
WHERE kglnaobj='P_TEST';
Owner Object HANDLE_ADDRESS
----------------------------------------------
SYS P_TEST 00000000EC7DA508
SQL> @getsid
ID SPID
----------------------------------
39 9685
bash-3.2# cat lib.d
#!/usr/sbin/dtrace -s -n
dtrace:::BEGIN
{
i = 1;
}
pid$1::kgllkal:entry,
pid$1::kglpnal:entry
{
printf("i =%d PID::entry:==%s:%s:%s:%s %x %x %x %x %x %x",i,probeprov, probemod, probefunc, probename,arg0,arg1,arg2,arg3,arg4,arg5);
i = i + 1;
}
SQL> exec p_test
…进行sleep等待
bash-3.2# ./lib.d 9685
dtrace: script './lib.d' matched 3 probes、
col PROGRAM for a30
col username for a10
col machine for a18
col event for a20
select a.sid,a.sql_id,a.event,a.username,a.machine,a.program,a.blocking_session,a.BLOCKING_INSTANCE,b.KGLPNMOD pin_mode, b.KGLPNREQ
from v$session a,x$kglpn b
where a.saddr=b.kglpnuse and b.kglpnhdl like '%00000000EC7DA508%'
/
SID SQL_ID EVENT MACHINE PIN_MODE KGLPNREQ
---------------------------------------------------------------------------------
39 96k96t6ymrh00 PL/SQL lock timer test 2 0
select a.sid,a.sql_id,a.event,a.username,a.machine,a.blocking_session,a.BLOCKING_INSTANCE,b.kgllkses, saddr,b.kgllkhdl handle,b.kgllkmod lock_mode,b.kglnaobj object
from v$session a ,x$kgllk b
where
a.saddr=b.KGLLKSES and
kgllkhdl like '%00000000EC7DA508%'
/
SID SQL_ID EVENT MACHINE HANDLE LOCK_MODE OBJECT
---------------------------------------------------------------------------------------------------
39 96k96t6ymrh00 PL/SQL lock timer test 00000000EC7DA508 1 P_TEST
其中PIN_MODE and LOCK_MODE
0-> 'None'
1-> 'Null'
2-> 'Share'
3-> 'Exclusive'
小结:
根据DTrace的过程可以得出,会话39执行存储过程p_test,其中在p_test的handle address上的请求过程为:
1)先请求share 模式的library cache lock,请求得到后,shared模式降至NULL模式,即我们查出显示 LOCK_MODE=1。
2)后继续请求share模式的library cache pin,请求得到后此会话正常运行p_test,但会话未运行结束所以share模式的library cache pin未释放,即我们查询p_test的handle address显示PIN_MODE=2。
3)即最后结果是,会话39在library cache中p_test的handle address持有NULL模式的library cache lock和share模式的library cache pin。
SQL> @getsid
SID SPID
----------------------------------
41 9702
bash-3.2# ./lib.d 9702
dtrace: script './lib.d' matched 3 probes
SQL> drop procedure p_test;
此进程处于waiting等待
SQL> col PROGRAM for a30
SQL> col username for a10
SQL> col machine for a18
SQL> col event for a20
SQL> select a.sid,a.sql_id,a.event,a.machine, b.KGLPNMOD pin_mode, b.KGLPNREQ
2from v$session a,x$kglpn b
3where a.saddr=b.kglpnuse and b.kglpnhdl like '%00000000EC7DA508%'
4/
SID SQL_ID EVENT MACHINE PIN_MODE KGLPNREQ
---------------------------------------------------------------------------------
41 4fjx9x3343wgf library cache pin test 0 3
39 96k96t6ymrh00 PL/SQL lock timer test 2 0
==LOCK_MODE
SQL> select a.sid,a.sql_id,a.event,a.machine,b.kgllkhdl handle,b.kgllkmod lock_mode,b.kglnaobj object
2from v$session a ,x$kgllk b
3where
4a.saddr=b.KGLLKSES and
5kgllkhdl like '%00000000EC7DA508%'
6 /
SID SQL_ID EVENT MACHINE HANDLE LOCK_MODE OBJECT
--------------------------------------------------------------------------------------------------
39 96k96t6ymrh00 PL/SQL lock timer test 00000000EC7DA508 1 P_TEST
41 4fjx9x3343wgf library cache pin test 00000000EC7DA508 3 P_TEST
分析小结:
根据DTrace的过程可以得出,会话41删除存储过程p_test,其中在p_test的handle address上的请求过程为:
1)首先申请Exclusive模式的library cache lock并已得到且不释放,从x$kgllk查询出LOCK_MODE=3得以证明。
2)然后申请Exclusive模式的library cache pin后处于等待状态,等待事件表现为library cache pin,从x$kglpn查询出KGLPNREQ=3得以证明。
3)即最后结果是,会话41在library cache中p_test的handle address持有Exclusive模式的library cache lock,并申请 Exclusive模式的library cache pin处于等待状态。
三、相关经典案例
XX年x月x日15:40左右某系统Oracle(3 nodes RAC)出现大量异常等待事件library cache pin,业务缓慢受堵,就此问题进行分析。
分析小结:
15:46:08开始,数据库突然出现大量的异常等待事件library cache pin并不断累积。
分析hanganalyze 文件XXXX2_diag_178650.trc,发现如下
发现等待的规律:
节点1的library cache pin 的对象地址都是在“0x116e454df0”上。
节点2的library cache pin 的对象地址都是在“0x11ce403120”上。
节点3的library cache pin 的对象地址都是在“0x11cf16bcb0”上。
根据x$kglob查询出对象名:
经过查询三个结点得出此对象地址都为XXXX.XX_PUBLIC
SQL> show parameter instance_name
NAME TYPE VALUE
------------------------------------------------------------------------------
instance_name string XXXX1
SQL> select kglnaown,kglnaobj from x$kglob
where kglhdadr like '%116E454DF0%';
KGLNAOWN KGLNAOBJ
----------------------------------------
XXXX XX_PUBLIC
节点2
SQL> show parameter instance_name
NAME TYPE VALUE
-------------------------------------------------------------------------------------------------
instance_name string XXXX2
SQL> select kglnaown,kglnaobj from x$kglob
where kglhdadr like '%11CE403120%';
KGLNAOWN KGLNAOBJ
-------------------- --------------------
XXXX XX_PUBLIC
节点3
SQL> show parameter instance_name
NAME TYPE VALUE
--------------------------------------------------------------------------------------------------
instance_name string XXXX3
SQL> select kglnaown,kglnaobj from x$kglob
where kglhdadr like '%11CF16BCB0%';
KGLNAOWN KGLNAOBJ
---------------------------------------
XXXX XX_PUBLIC
官方文档中说明:
Mode is the mode in which the pin is wanted. This is a number thus:
2 - Share mode.
3 - Exclusive mode.
分析library cache pin等待事件的p3(申请的锁模式),发现它们的共同点都是2,即这些被阻塞的会话想在XXXX.XX_PUBLIC申请shared 共享模式的锁,但被阻塞,由此可见有会话在XXXX.XX_PUBLIC上申请X排它模式的锁或持有X排它模式的锁未释放的情况,导致后面的会话无法申请shared共享锁而只能等待。
分析小结:
从hanganalyze分析,清楚的得出节点1、2、3的library cache pin等待事件都是在等待对象XXXX.XX_PUBLIC上,它们想申请shared 模式的锁但被阻塞。
根据前面分析怀疑有会话申请或持有XXXX.NEW_BUSI_PUBLIC的X排它模式的锁,导致后面申请share共享模式锁的会话被阻塞。
所以继续查询hanganalyze trace文件,发现节点2 的会话session 6199,18761发起对XX_PUBLIC的COMPILE编译的动作,信息如下:
Oracle session identified by:
{
instance: 2 (XXXX.XXXX2) <<<节点2
os id: 18761
process id: 1216, oracle@XXdb05.gmcc.net
session id: 6199 <<<会话ID 6199
session serial #: 18179
}
is waiting for 'library cache pin' with wait info:
{
p1: 'handle address'=0x11ce403120 <<< XX_PUBLIC过程
p2: 'pin address'=0x11a5dbbae0
p3: '100*mode+namespace'=0x2b5cefa00010003 <<< 申请X排它模式锁
time in wait: 11 min 24 sec
timeout after: 3 min 35 sec
wait id: 1091
blocking: 0 sessions
current sql: ALTER PACKAGE XX_PUBLIC COMPILE DEBUG
SPECIFICATION
short stack: ksedsts()+465<-ksdxfstk()+32<-ksdxcb()+1927<-sspuser()+112<-__sighandler()<-semtimedop()+10<-skgpwwait()+178<-ksliwat()+2047<-kslwaitctx()+163<-kjusuc()+3400<-ksipgetctxi()+1759<-kqlmPin()+2943<-kqlmClusterLock()+237<-kglpnal()+4059<-kglpin()+1381<-kkdllk0()+904<-kkdlGetCodeObject()+461<-kkpalt()+353<-opiexe()+18730<-opiosq0()+4310<-kpooprx()+274<-kpoal8()+842<-opiodr()+915<-ttcpip()+2183<-opitsk()+1705<-opiino()+969<-opiodr()+915<-opidrv()+570<-sou2o()+103<-opimai_real()+133<-ssthrdmain()+265<-main()+201<-__libc_
wait history:
* time between current wait and wait #1: 0.000049 sec
1.event: 'library cache lock' time waited: 0.000063 sec
wait id: 1090 p1: 'handle
address'=0x11ce403120
p2: 'lock address'=0x11ac983730
p3: '100*mode+namespace'=0x2b5cefa00010003
* time between wait #1 and #2: 0.000228 sec
2.event: 'library cache pin'
time waited: 0.000123 sec wait id: 1089
p1: 'handle address'=0xe9af65c00
p2: 'pin address'=0x11ac983730
p3: '100*mode+namespace'=0x10d6b00030002 * time between wait #2 and #3: 0.000037 sec
3.event: 'library cache pin' time waited: 0.000091 sec wait id: 1088
p1: 'handle address'=0xe881dc9c8
p2: 'pin address'=0x11ac983730
p3: '100*mode+namespace'=0x109e400030002
}
至此,问题根因已清楚明了,原因是节点2 的会话6199对包XXXX.XX_PUBLIC进行编译申请X排它模式的锁,而导致后面申请XXXX.XX_PUBLIC share共享模式锁的会话被阻塞,导致library cache pin等待事件的出现。
节点2 的会话6199的信息如下,是应用机器发起的对XXXX.XX_PUBLIC进行做编译的DDL操作。
分析小结
1)2017.08.09 15:46:08 节点2的会话6199对应的应用机器发起了对XXXX.XX_PUBLIC编译的DDL操作,导致从15:46:08 开始,节点1、2、3的会话无法共享访问XXXX.XX_PUBLIC而出现library cache pin等待事件。
2)此故障为典型的Library cache pin案例,是在业务高峰期时对包XXXX.XX_PUBLIC编译的DDL操作导致,oracle不建议在业务高峰期对访问频繁的对象进行编译或其它DDL操作,因为这些操作很有可能会导致shared pool中lath的争用,从而导致大量异常事件出现而影响数据库性能。
如果字段的最大可能长度超过255字节,那么长度值可能…
只能说作者太用心了,优秀
感谢详解
一般干个7-8年(即30岁左右),能做到年入40w-50w;有…
230721