第一小结 library cache 的内存结构
Library cache的作用,最主要的就是存储已解析的SQL声明,避免硬解析。我们可以通过一个视图V$librarycache来了解Library cache点中率,已此来评估SQL声明的解析情况。应时时注意此视图中点中率情况,好及时发现并解决问题。
一、Library cache中所存储的信息:
1. 按对象类型分类:
共享游标(SQL and PL/SQL objects)、数据库对象(tables, indexes, and so on)
2. 按存在时间分类:
存贮对象:如表、索引、视图等(老化后 磁盘上还有 所以叫永久存储 并不是永久存储在LIBRARY 中)
瞬时对象:如游标(老化后就没有了)(简单了解在Library cache中数据库对象的信息是什么)
二、Library cache中如何存储信息
了解Library cache的简单原理,对于理解一些调优概念,是十分必要的。
共享池内存的结构,是计算机中常用的哈希表形式的扩展。常用的哈希表形式,总是先有一个哈希表,保存对象地址(或句柄),然后,根据对象地址(或句柄)访问对象,计算机中常以这种方式组织数据。其基本形式如下:
[attach]292840[/attach]
注意地址和句柄的区别,一般来说,地址只是一个位置信息。而句柄,除了位置,还包含一些其他信息。
还要注意“堆(Heap)”的概念,程序开发者在程序员使用系统函数分配的内存。
注:此处补充一点概念,除了堆之外,还有栈,也是内存中比较常见的名词。开发者在程序中定义的各种变量,就在栈中分配。
上图每一组哈希值、链表头,叫做一个哈希桶。简单的说就是:哈希桶(Hash Bucket)指向对象句柄(Object Handles),对象句柄存有对象所占的堆内存的地址。由于对象的堆往往不只一个,Oracle习惯称这些堆为子堆。通常,对象句柄中存有0号子堆的地址,而0号子堆中存有其他各个子堆的地址,而Library cache中所存贮对象的信息,就在从0号开始的各个子堆中。
第二小节 Library cache的PIN与LOCK
一、详述Library cache lock与Library cache pin
1.
Library cache lock与Library cache pin的模式。
所有在Library cache中的对象,都由两部分组成,一个句柄、至少一个子堆。这一点可以参考上面的图。
句柄中记录的有对象的名字、命名空间、Lock的持有者和等待者、Pin的持有者和等待者、一些标志信息,最重要的,句柄中记有堆的地址。
在Library cache中寻找对象时,先计算HASH值,在HASH表中找到句柄,再经由句柄,找到对象实际的内存地址。在这个过程中,有两个重要的数据项需要被锁保护起来。一个是对象句柄、另一个就是对象的内存堆。在对象句柄上加的锁就是Library cache lock,在内存堆上加的锁,就是Library cache pin。
下面先讨论一下Library cache lock。
(1)。 Library cache lock
Locks 除了阻止不相容的对句柄的访问,以保护句柄中数据的完整性外,获得Locks也是在缓存中定位对象的唯一方式,即:进程在对句柄上加锁的同时,完成在内存中定位堆的操作。
在句柄上获得Lock、并在内存中定位到堆后,对象可以Pin自己的堆。如果对象相关信息不在内存中,Pinning一个对象将导致它和它的的子堆被装载(此种情况时,如果是多个对象Pin一个对象,将可能会造成Pin等待)
Lock 有三种模式
· Share(s) : 读对象
· Exclusive(x) : 修改或创建对象
· Null(n) : 专用于为会话持续
注意: 存贮对象可以被锁在以上任意一种方式,瞬时对象只能被锁在Null方式。
Null 锁在执行SQL声明的解析阶段被获得,此后一直持有。它不阻止任何DDL。也用术语“易碎解析锁”称乎它(breakable parse lock)。
在以下两种情况下Null锁被打碎:
· 当锁所在对象有一个独占Pin时
· 锁所在对象的任何依赖对象有一个独占Pin时
(2)。Library cache pin:
Pin有两种模式:
· Share (s) : 读一个对象堆
· Exclusive (x) : 修改一个对象堆
无论存贮对象还是瞬时对象,都能被Pinned在Share或Exclusive模式。当修改对象时,进程首先会以Share模式Pin对象,进行错误和安全检查,然后在以Exclusive模式Pin住对象。Pin的解除将会导致相关对象上的易碎锁Break
不同类型的操作所需要的不同类型的lock/pin:
1).
所有的DDL操作都会在需要处理的对象上放一个Exclusive(排他)类型的Lock和Pin(仅仅当执行的时候加上)。
如:重编译,截断表,给对象授权,等等
2).
所有对对象的使用都需要一个null类型lock和shared类型的pin(仅仅当执行的时候加上)。如:使用视图,执行过程,等等。
以上规则,也同样应用于对象所有依赖的对象。如:一个依赖于其他视图的视图,一个依赖于其他包的包。
下面我们用一个例子,来验证一下Library cache lock/pin。
例10:观察Library cache lock/pin 的状态:
先建立一下如下过程:
create or replace procedure jj_cur is
cursor aa is select kglhdlmd,kglhdpmd
from x$kglob where kglnaobj='select * from aa_1 where id=1' and kglhdadr<>kglhdpar;
kk number:=5;
kk1 number:=5;
begin
for i in 1..2000 loop
kk:=5;
kk1:=5;
open aa;
fetch aa into kk,kk1;
dbms_output.put_line(kk||'-'||kk1);
close aa;
end loop;
end;
/
Kglhdadr是游标句柄,而Kglhdpar是父游标句柄,条件中的kglhdadr<>kglhdpar ,目的是只显示子游标。Kglhdlmd是Library cache lock的模式,为0时表示没有锁,1是NULL锁,2是共享锁,3是独占锁。Kglhdpmd是Library cache pin的模式,0是没有Pin,2是共享Pin,3是独占Pin。
另外,我们作为测试用的SQL声明select * from aa_1 where id=1,要保证只有一个子游标,这样做的目的,就是让过程中的游标AA只选出来一条记录。在过程执行完毕后,最好再执行声明:select kglhdlmd,kglhdpmd from x$kglob where kglnaobj='select * from aa_1 where id=1' and kglhdadr<>kglhdpar一次,看一下能选出来几行,如果多于一行,说明声明子游标的数量多于一条,应该换一条声明再试。
在会话A中:
spool e:\oracle\aaa.txt
exec jj_cur;
在会话B中:
select * from aa_1 where id=1;
在会话A中:
spool off
查看e:\oracle\aaa.txt,可以看到,在会话B的声明执行时:Library cache lock先是1,然后是0。而Library cache pin先是3,后是2,然后是0。会话B中的声明是第一次执行,这是硬解析,所以会有很短时间的独占Pin。上面的例子,可以再试一次,这次,就是软解析了。试验过程同上,结果是,Library cache lock仍是1,然后是0,而Library cache pin则是2,然后是0,不再有3了。
我们可以再用上面的方法,测试一下父游标上的锁的情况,这里就略过了。
我记得有资料上说,声明在解析时,在父、子游标上会有独占锁的,但是在上面的测试中,却没有看到。有可能是我的过程执行速度不过快,无法捕找到句柄上的独占锁。