网络通信 频道

广电再出狠招 IPTV前途坎坷

......接上篇

2.2 转储library cache
oracle提供了命令可以对library cache中的内容进行转储。于是我们可以对library cache进行转储,从而对上面所说的library cache的内容进行验证。
ALTER SESSION SET EVENTS 'immediate trace name library_cache level N';
这里的N可以取的值分别为:
1 转储library cache的统计信息
2 转储hash表的汇总信息
4 转储library cache object的基本信息
8 转储library cache object的详细信息
16 转储heap size的信息
32 转储heap的详细信息
在测试之前,我们先创建一个测试表,然后再显示该表的数据。从而在library cache中放入一些数据。
SQL> create table sharedpool_test as select * from dba_objects where rownum<10;
SQL> select object_id,object_name from sharedpool_test;
以level 1转储整个library cache。
SQL> ALTER SESSION SET EVENTS 'immediate trace name library_cache level 1';

    打开跟踪文件可以看到类似这样的信息,这实际就是v$librarycache里记录的信息,只不过v$librarycache中记录的是根据下面的信息合并汇总以后得到的。

namespace gets hit ratio pins hit ratio reloads invalids
-------------- --------- --------- --------- --------- ---------- ----------
CRSR 563 0.815 2717 0.916 15 0
TABL/PRCD/TYPE 403 0.730 568 0.653 0 0
BODY/TYBD 2 0.000 2 0.000 0 0
……………………
    然后,我们分别以level 4、8、16、32分别对library cache进行转储,生成的转储文件分别以4#、8#、16#和32#来表示。
打开4#文件,然后直接查找“select object_id,object_name from sharedpool_test”,因为我们前面说到过,对于SQL语句来说,整个SQL语句的文本就是library cache object的名称。于是,我们可以发现类似下图四所示的内容: 



      图四
    这里的BUCKET 62658就相当于图二中的2号bucket。该bucket上只挂了一个对象,其对象句柄号为6758cdbc。在这个对象句柄里存放了很多信息,这里可以看到该对象的namespace为CRSR,也就是SQL AREA。可以看到该SQL语句的hash值为541cf4c2,将其转换为十进制以后可以直接到v$sql中找到该SQL语句。我们还可以看到很复杂的flags字段,它会包括很多标记,比如RON表示只读(Read Only),SML表示当前句柄尺寸比较小(Small)等。而下面的lwt则表示正在等待lock的对象列表(Lock Waiters),对应图三中的“Lock Waiters”;ltm则表示临时正在持有lock的对象列表(Lock Temporary),对应图三中的“Lock Owners”;pwt则表示正在等待pin的对象列表(Pin Waiters)对应图三中的“Pin Waiters”;ptm则表示临时正在持有pin的对象列表(Pin Temporary),对应图三中的“Pin Owners”。再往下看,可以看到CHILDREN部分,这部分就是前面所说过的子游标的信息了。实际上,指向heap 0的指针也位于这一部分,这个指针也就是6758c840。
 
SQL> select sql_text from v$sql where hash_value=to_number('541cf4c2','xxxxxxxx');
SQL_TEXT
--------------------------------------------------------------------------------
select object_id,object_name from sharedpool_test

     然后,我们打开8#文件,查找6758c840,可以看到如下图五所示的内容。这就是heap 0中所包含的内容。可以看到该heap 0的handle正是6758c840,type为CRSR。还可以看到几个重要的table,这些table都是我们前面介绍过的,包括DEPENDENCIES、ACCESSES、TRANSACTIONS。从前面我们已经知道dependency table记录的是SQL语句所依赖的对象,这里我们可以看到我们的SQL语句依赖一个对象,同时该对象的handle为 675d0d74,很明显,它一定指向sharedpool_test表。同时,我们可以看到transaction table所记录的oracle底层解析的对象的handle也是675d0d74,它与dependency table所记录的对象是一样的,说明这个表是实实在在的表,而不是一个同名词。
 
  


                 图五
    于是我们继续在8#文件里查找675d0d74,也就是找到library cache中记录SQL所引用的对象的部分。
我们可以看到类似下图六所示的内容。从name列中可以看到,该对象正是sharedpool_test表,同时该表所在的schema为COST。而且从type为TABL也可以看到,对象sharedpool_test是一个表。 

    

     图六
    我们再次回到图五,也就是记录heap 0的部分。我们可以看到最后一部分是DATA BLOCKS,从我们前面介绍过的内容可以知道这部分的记录指向了其他的heap内存块。我们从data#列上可以知道,该SQL存在两个相关的heap,编号为0和6。我们知道,heap 0存放了SQL语句本身所涉及到的对象以及若干种表等的信息,而heap 6则存放了SQL语句的文本、执行计划等。于是,我们可以到32#文件中查找6758c7d0(heap 0)和67587c34(heap 6),如下图七所示。我们同时可以看到owner的值,实际上这正是在图五中的object的代号。同时从heap的name处也可以看到,heap 0为library cache,而heap 6为sql area,这也说明了这两个不同的heap所存放的不同内容。 

    


    图七


2.3 dictionary cache概述
    dictionary cache专门用来存放SYS schema所拥有的对象的内存区域。使用dictionary cache时以行为单位,而不像其他比如buffer cache以数据块为单位,因此dictionary cache也叫做row cache。构造dictionary cache的目的是为了加快解析SQL语句的速度,因为dictionary cache里存放了所有表的定义、Storage信息、用户权限信息、约束定义、回滚段信息、表的统计信息等。
    而这些信息都是在解析过程中必须用到的。假设oracle在解析SQL的过程中,发现dictionary cache里没有该SQL所引用的表的定义信息,则oracle必须到磁盘上system表空间里找到这个引用表的定义信息,并将这些定义信息加载到dictionary cache里。这个从磁盘上获取数据字典数据的过程就叫做递归SQL(Recursive SQL)。通常来说,当我们执行一条新的SQL语句时,都会产生很多次的递归调用,也会产生很多的递归SQL。比如我们来下面这个例子。 
   SQL> set autotrace traceonly stat;
   SQL> select * from sharedpool_test;
   Statistics
   ----------------------------------------------------------
    185 recursive calls
    0 db block gets
    25 consistent gets
   ………… 

    从这里可以很明显看到执行该SQL产生了185次的递归调用,这185次的递归调用将表sharedpool_test相关的信息,比如列定义、统计信息等,都加载到了dictionary cache里。当我们再次执行该SQL时,会发现recursive calls变成了0,因为dictionary cache里已经包含解析SQL所需要参照的数据字典了。 
  

转储dictionary cache
  我们可以使用如下命令对dictionary cache进行转储。
  ALTER SESSION SET EVENTS 'immediate trace name row_cache level N';

这里的N可以取的值分别为:

1 转储dictionary cache的统计信息 ;
2 转储hash表的汇总信息 ;
8 转储dictionary cache中的对象的结构信息;

  如果对level 1进行转储,可以看到转储出来的内容,很明显,就是v$rowcache里的内容。每一种数据字典都有一行记录来表示。比如有tablespace相关的数据字典等。

  如果以level 2转储的话,可以看到类似如下的内容。这里有23个hash表对dictionary cache中的对象进行管理,每个hash表都对应了一种数据字典,同时有一个名为row cache objects的latch来控制并发访问。可以看到,v$latch_children里名为“row cache objects”的记录数量也是23。

ROW CACHE HASH TABLE: cid=0 ht=66BD90B0 size=32

…………

ROW CACHE HASH TABLE: cid=1 ht=66BD78B0 size=256

…………

ROW CACHE HASH TABLE: cid=22 ht=66DA5590 size=512

…………

shared pool的内部管理机制
3.1解析SQL语句的过程
为了将用户写的可读的SQL文本转化为oracle认识的且可执行的语句,这个过程就叫做解析过程。
解析分为硬解析和软解析。当一句SQL第一次被执行时必须进行硬解析。
当客户端发出一条SQL语句(也可以是一个存储过程或者一个匿名PL/SQL块)进入shared pool时
(注意,我们从前面已经知道,oracle对这些SQL不叫做SQL语句,而是称为游标(cursor)。因为oracle在处理SQL时,需要很多相关的辅助信息,这些辅助信息与SQL语句一起组成了游标),oracle首先将SQL文本转化为ASCII字符,然后根据hash函数计算其对应的hash值(hash_value)。根据计算出的hash值到library cache中找到对应的bucket,然后比较bucket里是否存在该SQL语句。
如果不存在,则需要按照我们前面所描述的,获得shared pool latch,然后在shared pool中的可用chunk链表(也就是bucket)上找到一个可用的chunk,然后释放shared pool latch。在获得了chunk以后,这块chunk就可以认为是进入了library cache。然后,进行硬解析过程。硬解析包括以下几个步骤:
1) 对SQL语句进行语法检查,看是否有语法错误。比如没有写from等。如果有,则退出解析过程。
2) 到数据字典里校验SQL语句涉及的对象和列是否都存在。如果不存在,则退出解析过程。
3) 将对象进行名称转换。比如将同名词翻译成实际的对象等。如果转换失败,则退出解析过程。
4) 检查游标里用户是否具有访问SQL语句里所引用的对象的权限。如果没有权限,则退出解析过程。
5) 通过优化器创建一个最优的执行计划。这一步是最消耗CPU资源的。
6) 将该游标所产生的执行计划、SQL文本等装载进library cache的若干个heap中。
在硬解析的过程中,进程会一直持有library cach latch,直到硬解析结束。硬解析结束以后,会为该SQL产生两个游标,一个是父游标,另一个是子游标。父游标里主要包含两种信息:SQL文本以及优化目标(optimizer goal)。父游标在第一次打开时被锁定,直到其他所有的session都关闭该游标后才被解锁。当父游标被锁定的时候是不能被交换出library cache的,只有在解锁以后才能被交换出library cache,这时该父游标对应的所有子游标也被交换出library cache。子游标包括游标所有的信息,比如具体的执行计划、绑定变量等。前面图四中看到的CHILDREN部分就是子游标所对应的handle的信息。子游标随时可以被交换出library cache,当子游标被交换出library cache时,oracle可以利用父游标的信息重新构建出一个子游标来,这个过程叫reload。可以使用下面的方式来确定reload的比率:
SELECT 100*sum(reloads)/sum(pins) Reload_Ratio FROM v$librarycache;
一个父游标可以对应多个子游标。子游标具体的个数可以从v$sqlarea的version_count字段体现出来。而每个具体的子游标则全都在v$sql里体现。当具体的绑定变量的值与上次的绑定变量的值有较大差异(比如上次执行的绑定变量的值的长度是6位,而这次执行的绑定变量的值的长度是200位)时或者当SQL语句完全相同,但是所引用的对象属于不同的schema时,都会创建一个新的子游标。
如果在bucket中找到了该SQL语句,则说明该SQL语句以前运行过,于是进行软解析。软解析是相对于硬解析而言的,如果解析过程中,可以从硬解析的步骤中去掉一个或多个的话,这样的解析就是软解析。软解析分为以下三种类型。
1) 第一种是某个session发出的SQL语句与library cache里其他session发出的SQL语句一致。这时,该解析过程中可以去掉硬解析中的5和6这两步,但是仍然要进行硬解析过程中的2、3、4步骤:也就是表名和列名检查、名称转换和权限检查。
2) 第二种是某个session发出的SQL语句与library cache里该同一个session之前发出的SQL语句一致。这时,该解析过程中可以去掉硬解析中的2、3、5和6这四步,但是仍然要进行权限检查,因为可能通过grant改变了该session用户的权限。
3) 第三种是当设置了初始化参数session_cached_cursors时,当某个session对相同的cursor进行第三次访问时,将在该session的PGA里创建一个标记,并且该游标即使已经被关闭也不会从library cache中交换出去。这样,该session以后再执行相同的SQL语句时,将跳过硬解析的所有步骤。这种情况下,是最高效的解析方式,但是会消耗很大的内存。
我们先来举一个例子说明如果在解析过程中发生语法或语义错误时,在shared pool中是怎样体现的。
SQL> select object_type fromm sharedpool_test111;
ORA-00942: 表或视图不存在
然后我们以level 16转储library cache,并打开转储文件,找到相应的部分,如下图八所示。可以看到,
该SQL语句在语法上是错误的(from写成了fromm),oracle仍然在shared pool中为其分配了一个chunk,然后该chunk进入library cache,并在library cache中分配了一个bucket,同时也生成了heap 0,但是该heap 0中不存在相应的一些如dependency table等table的部分,以及data block的部分。我看到有些资料上说SQL语句是先进行语法分析,如果通过语法分析以后,则应用hash函数生成hash值,然后再去shared pool中分配chunk。实际上从这个实例已经可以看出,这个说法是错误的。oracle始终都是先对SQL生成hash值(不论该SQL语法上是否正确),再根据hash值到对应的可用chunk链表(也就是bucket)里分配chunk,然后进入语法解析等解析过程。




图八
我们再举一个例子来说明解析正确的SQL语句的过程。如下所示。
SQL> alter system flush shared_pool;
SQL> variable v_obj_id number;
SQL> exec :v_obj_id := 4474;
SQL> select object_id,object_name from sharedpool_test where object_id=:v_obj_id;
OBJECT_ID OBJECT_NAME
---------- ---------------------------
4474 AGGXMLIMP
SQL> variable v_obj_id varchar2(10);
SQL> exec :v_obj_id := '4474';
SQL> select object_id,object_name from sharedpool_test where object_id=:v_obj_id;
OBJECT_ID OBJECT_NAME
---------- ---------------------------
4474 AGGXMLIMP
然后,我们以level 16来转储library cache。可以看到如下图九所示的内容。很明显的看到,子游标的
部分包含两条记录,这也就说明该SQL语句产生了两个子游标。虽然我们从SQL文本上看,前后两次执行的SQL语句是一样的。只有绑定变量的类型发生了改变,第一次是number型,而第二次是varchar2型。可正是这数据类型的变化导致了该SQL语句的执行计划不能得到共享,从而产生了两个子游标。这时,我们根据子游标的两个handle:6757f358和674440fc找到对应的heap 0的话,就可以看到这两个heap 0中所记录的heap 6是两个完全不同的内存块,这也说明前后两次执行SQL并没有真正得到共享。



图九

我们还可以根据该SQL的hash值(f390fb6f)来看看动态性能视图里是如何表现的。
SQL> select to_number('f390fb6f','xxxxxxxx') from dual;
TO_NUMBER('F390FB6F','XXXXXXXX
------------------------------
4086365039
SQL> select sql_text,version_count from v$sqlarea where hash_value=4086365039;
SQL_TEXT VERSION_COUNT
------------------------------------------------------------------------- ------------
select object_id,object_name from sharedpool_test where object_id=:v_obj_id 2
SQL> select sql_text,child_address,address from v$sql where hash_value=4086365039;
SQL_TEXT CHILD_ADDRESS ADDRESS
-------------------------------------------------------------------- ----------- --------
select object_id,object_name from sharedpool_test where object_id=:v_obj_id 6757F358 676B6D08
select object_id,object_name from sharedpool_test where object_id=:v_obj_id 674440FC 676B6D08
从记录父游标的视图v$sqlarea的version_count列可以看到,该SQL语句有2个子游标。而从记录子游标的视图v$sql里可以看到,该SQL文本确实有两条记录,而且它们的SQL文本所处的地址(address列)也是一样的,但是子地址(child_address)却不一样。这里的子地址实际就是子游标所对应的heap 0的句柄。
由此我们也可以看到,存在许多因素可能导致SQL语句不能共享。常见的因素包括, SQL文本大小写不一致、SQL语句的绑定变量的类型不一致、SQL语句涉及到的对象名称虽然一致但是位于不同的schema下、SQL的优化器模式不一致(比如添加提示、修改了optimizer_mode参数等)等。

3.2 library cache的并发控制
  由于library cache是可以被所有进程同时访问并修改的,因此就必然存在一个并发控制的问题。比如对于前面我们举的如图九所示的例子来说,第一次使用number类型的绑定变量执行的SQL语句产生的游标挂在bucket 64367上。而当我们第二次使用varchar2类型的绑定变量再次执行该SQL语句时,oracle需要访问该bucket 64367上的句柄,发现不能共用执行计划时,还要修改该句柄,向CHILDREN部分添加一条指向另外一个子游标的句柄。在很多进程并发执行的情况下,那么当修改挂在bucket 64367上的句柄的时候,必须防止其他进程同时修改所访问的句柄。因为如果不防止这种情况的出现,那么假如这时正好也有一个进程也是使用varchar2类型的绑定变量执行该相同的SQL时,那么也会向CHILDREN部分添加一条子游标的记录,那么CHILDREN部分就会出现两条varchar2的执行计划,而实际上这两个执行计划是一样的,是完全可以合二为一的,这样也就达不到SQL共享的目的。同时还要考虑,当oracle在向某个heap(比如heap 0和heap 6)填入SQL文本、执行计划等数据的过程中,要防止该heap又被其他进程分配掉的情况出现。如果不防止的话,那这个heap的数据就被两个进程同时写,那里面的数据一定是混乱而无法使用的了。

    为了有效的解决上面所说的并发性的问题,oracle使用三种结构来完成对library cache的并发控制:lock、pin和library cache latch。简单来说,进程如果要访问或者修改library cache里的对象,首先必须获得library cache latch,然后获得handle上的lock,最后获得heap上的pin,访问或修改结束以后,释放pin、lock和latch。

  lock是落在library cache里的对象句柄上的,用来管理并发性。按照前面所说的例子,当多个进程同时修改bucket 64367上的句柄的时候,只有一个进程能够获得该句柄上的lock,其他进程必须等待(体现的等待事件就是library cache lock)。同时,尝试获得某个句柄上的lock也是将游标句柄对象加载到shared pool里的唯一方式。也就是说,当客户端发出某个SQL语句时,oracle对该SQL语句运用hash函数生成hash值,然后到该hash值所对应的library cache的bucket里试图找到对应的句柄并lock该句柄时,如果发现该句柄不存在(可能是由于该SQL语句是一条全新的SQL语句,或者以前该SQL语句执行过但是现在被交换出了library cache),则会将该SQL游标所对应的句柄加载到library cache里。

    目前有三种lock模式,分别是:share、exclusive和null。如果某个进程只是要读取句柄里的信息时,会对该句柄添加share模式的lock,比如当编译某个存储过程时,进程会去读取该存储过程所引用的子存储过程等,这时其他进程可以对该相同的句柄添加share和null模式的lock;如果某个进程需要修改对象里的信息时,就会对该句柄添加exclusive模式的lock,比如删除某个存储过程就会添加exclusive模式的lock,这时其他进程只能对该相同的句柄添加null模式的lock;null模式的lock比较特殊,在任何可以执行的对象上(比如存储过程、视图、函数等等)都存在该null模式的lock。你可以随意打破该模式的lock,这时该lock所在的对象就失效了,需要重新编译。当SQL开始解析时,或获得null模式的lock,然后会一直加在该对象上,直到某些会引起对象失效的DDL发生在对象或对象所依赖的其他对象上,这时该lock被打破。当发生null模式的lock时,其他进程可以对该相同的句柄添加任何模式的lock。

    而pin则是落在heap上的,用来防止多个进程同时更新同一个heap。pin的优先级比lock要低,获得pin之前必须先获得lock。同样按照前面所说的例子,当第二次使用varchar2类型的绑定变量执行相同的SQL语句时,该进程首先会获得bucket 64367的句柄上的lock,根据该句柄里所记录的heap发现不能共用时,到shared pool中分配可用的chunk作为heap(包括heap 0和heap 6等)的空间,并获得该heap上的pin,然后在句柄里添加一条子游标记录,以指向所分配的heap 0的句柄。当pin住了heap以后,进程就向heap中写入数据,结束以后释放pin,最后释放lock。当某个进程获得了句柄上的lock,但是不能pin住该句柄所对应的heap时,该进程就必须等待(体现的等待事件就是library cach pin)。与lock相同,当进程试图pin住某个heap但是发现该heap不存在时,就会同时将该heap加载到library cache里同时pin住它。

    pin有两种模式:share和exclusive。当某个进程只需要读取heap中的信息时,会对该heap执行share模式的pin。如果进程需要修改heap时,则会先对该heap执行share模式的pin以便对heap进行错误和安全检查,通过以后,再对该heap执行exclusive模式的pin,从而对该heap进行修改。

    从上面对lock和pin的描述中可以看出,lock本身不是一个原子的操作,也就是说要完成lock需要执行一系列的操作步骤(包括pin住heap等)。因此为了防止lock的过程被其他进程打破,oracle使用library cache latch来管理lock。也就是说,如果某个进程在进行lock之前,必须先获得library cache latch,如果不能获得该latch,就必须等待。当lock过程结束以后,释放该latch。
oracle提供了多个library cache latch(这样,每个library cache latch都称为子latch)来保护library cache中的bucket。这些子latch的数量由一个隐藏参数决定:_kgl_latch_count。该参数缺省值为大于等于系统中CPU个数的最小的素数。比如在一个具有4个CPU的生产环境中,library cache latch的个数为5,如下所示。但是oracle内部(9i版本)规定了library cache latch的最大个数为67,即便将这个隐藏参数设置为100,library cache latch的数量也还是67个。
SQL> select x.ksppinm, y.ksppstvl, x.ksppdesc
2 from x$ksppi x , x$ksppcv y
3 where x.indx = y.indx
4 and x.ksppinm like '\_%' escape '\'
5 and ksppinm like '%kgl_latch_count%'
6 ;
KSPPINM KSPPSTVL KSPPDESC
-------------------- ---------- ----------------------------------------
_kgl_latch_count 5 number of library cache latches

具体到每个bucket应该由哪个子latch来管理,则是通过下面这个函数来确定的。
latch号=mod(bucket号,latch的数量)
假如还是按照上面的例子,对于bucket 64367来说,假设当前系统具有37个library cache latch,那么会使用24(mod(64367,37)=24)号latch来保护挂在该bucket上的句柄。正是由于这样的算法,可能会导致所有的子latch不能在library cache里的整个bucket链条上均匀分布,有可能出现某个或某几个子latch非常繁忙,而有些子latch则非常空闲。至于如何判断以及解决,可以见下面shared pool的优化部分。
我们来做两个测试,分别来模拟一下lock和pin。来看看lock和pin是如何控制library cache里的对象的。试验的思路很简单,第一,打开一个session(sess #1)创建一个存储过程,该过程只做一件事情,就是通过调用dbms_lock.sleep进行等待。并在sess #1中调用该存储过程;第二,打开第二个session(sess #2),重新编译该存储过程;第三,打开第三个session(sess #3),删除该存储过程;第四,打开第四个session(sess #4)进行监控。根据前面对lock和pin的描述,我们可以预见,sess #2将等待library cache pin。而sess #3会等待library cache lock。
试验过程如下,在试验的过程中,不断以level 16转储library cache以更深入的观察lock和pin的变化,以下按照时间顺序排列:
sess #1
SQL> create or replace procedure lock_test
2 is
3 begin
4 sys.dbms_lock.sleep(5000);
5 end;
6 /
SQL> exec lock_test;
sess #4,转储出来的文件编号为F1。
SQL> ALTER SESSION SET EVENTS 'immediate trace name library_cache level 16';
sess #2
SQL> select sid from v$mystat where rownum=1;
SID
----------
9
SQL> alter procedure lock_test compile; --这时该命令停住了。
sess #4,转储出来的文件编号为F2。
SQL> ALTER SESSION SET EVENTS 'immediate trace name library_cache level 16';
sess #3
SQL> select sid from v$mystat where rownum=1;
SID
----------
10
SQL> drop procedure lock_test; --这时该命令也停住了
sess #4,转储出来的文件编号为F3。
SQL> ALTER SESSION SET EVENTS 'immediate trace name library_cache level 16';
SQL> select sid,event from v$session_wait where sid in(9,10);
SID EVENT
---------- ----------------------------------------------------------------
9 library cache pin
10 library cache lock
从监控的结果看到,正如我们所预料的,编译存储过程的sess #2(sid为9)正在等待library cache pin,而删除存储过程的sess #3(sid为10)正在等待library cache lock。在转储出来的文件中,我们主要关存储过程lock_test本身在library cache中的变化。在F1中,我们可以看到如下图十的内容。注意其中的lock为N,pin为S。由于sess #1正在执行lock_test存储过程,需要读取该handle所对应的heap里的内容,因此以null模式lock住句柄,同时以share模式pin住了heap。这时该对象句柄可以被其他进程以任何模式锁定,但是该句柄对应的heap只能被其他进程以share模式pin,而不能以exclusive模式pin。




图十
我们打开发出编译命令以后生成的F2,找到与图十同样的部分,如下图十一所示。由于sess #2会对存储过程lock_test进行编译,因此需要重新刷新该对象的heap中的信息。所以需要以exclusive模式lock住该对象的句柄,同时以exclusive模式pin住该对象的heap。这时,由于当前句柄上存在null模式的lock,因此sess #2申请exclusive的lock能够成功,但是由于当前该句柄对应的heap上已经存在share的pin,因此申请exclusive的pin时,必须等待,这时体现为sess #2等待library cache pin。 



     图十一

    这时,我们发出删除命令以后,很明显的,要删除存储过程lock_test,sess #3必须以exclusive模式获得lock_test句柄的lock。而这时该句柄上已经存在了exclusive模式的lock,于是这时sess #3只有等待sess #2所添加的exclusive模式的lock释放以后才能继续进行。体现在v$session_wait等相关视图里就是等待library cache lock。这时,我们甚至可以发现,整个“drop procedure lock_test”命令都没有出现在library cache里。也就是说,oracle已经为该SQL语句分配了chunk,但是由于无法获得所引用对象的lock,从而使得所分配的chunk还没有能够挂到bucket上去,也就还没有进入library cache里。
至于如何诊断以及解决这两个等待事件的话,可以见下面shared pool的优化部分。
   (待续......)
0
相关文章