解析Oracle library cache 内存结构

梁铭图 2019-06-22 21:14:00
 

​作者介绍

梁铭图,新炬网络首席架构师,十多年数据库运维、数据库设计、数据治理以及系统规划建设经验,拥有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内存结构了解

 

1、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对象的信息,还包括这些对象所依赖的表、索引等对象的信息。我们可以通过相关视图一探究竟:

 

2、library cache内存结构
 

 



以上这图可看出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里的内容以作梳理。

 

3、查询验证
 

 

执行一条测试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的并发访问的。

 

我们可以用以下的实验来模拟这个过程:

 

1、创建存储过程P_ TEST,并查询出其handle address
 

 

 

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

 

2、当前等待事件正常
 


 

3、连接一个会话
 

 

 

SQL> @getsid

ID    SPID

----------------------------------

39    9685

 

4、利用DTrace跟踪会话39
 

 

 

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;

}

 

5、会话39执行p_test存储过程
 

 

 

SQL> exec p_test

…进行sleep等待

 

6、分析9685的DTrace跟踪内容
 

 

 

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。

 

7、连接另一个会话题
 

 

 

SQL> @getsid

SID   SPID

----------------------------------

41    9702

 

8、利用DTrace跟踪会话41
 

 

 

bash-3.2# ./lib.d 9702

dtrace: script './lib.d' matched 3 probes

 

9、会话41删除p_test存储过程
 

 

 

SQL> drop procedure p_test;

此进程处于waiting等待

 

10、查询等待事件,library  cache  pin出现
 

 


 

11、分析9702的DTrace跟踪内容二级标题
 



 

 

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处于等待状态。

 

三、相关经典案例

 

1、问题描述
 

 

XX年x月x日15:40左右某系统Oracle(3 nodes RAC)出现大量异常等待事件library cache pin,业务缓慢受堵,就此问题进行分析。

 

2、故障现象
 

 





分析小结:

 

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 模式的锁但被阻塞。

 

3、library cache pin根因
 

 

根据前面分析怀疑有会话申请或持有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的争用,从而导致大量异常事件出现而影响数据库性能。

 

最新评论
访客 2024年04月08日

如果字段的最大可能长度超过255字节,那么长度值可能…

访客 2024年03月04日

只能说作者太用心了,优秀

访客 2024年02月23日

感谢详解

访客 2024年02月20日

一般干个7-8年(即30岁左右),能做到年入40w-50w;有…

访客 2023年08月20日

230721

活动预告