21、library cache_1(library cache lock/pin、cursor pin X)
latch用来保护链的,同时限制了并发
硬解析(shared pool、library cache、latch)
图解:
绝大部分情况下,对于library cache来讲,它是以读的方式来持有latch;
对于硬解析来讲,还有一个shared pool,挂的是一个一个的chunk;当发生硬解析的时候,oracle会在shared pool里面找到一个他需要的chunk,server process1根据自己需要chunk的大小,持有一个latch,然后在shared pool里面找到一个chunk,找到chunk以后,它需要在里面写东西(一个SQL硬解析完了,需要把SQL和执行计划写到里面去),写完以后,修改对应的链,然后把写了SQL和执行计划的chunk,从链上拿下来,然后挂到library cache里的其中一个链上;挂到library cache里的其中一个链上的时候,也要修改library cache的链,所以对于shared pool和library cache都需要以X的方式持有着latch,这就很有可能与S方式持有着的latch产生冲突;
所以,如果有很多的硬解析的话,就会出现严重的shared pool的latch争用和library cache的latch争用;
再有就是,假设SQL需要的chunk是6k,然后找到一个8k的chunk,还剩2k,2k又挂到2k的链上,这个6k的chunk被拿下来就没了;然后另外一个SQL过来,需要14k,找到一个16k的chunk,剩下2k,又把剩下的2k挂到2k的链上,这个14k的chunk被拿下来也没了;随着时间的推移,硬解析越来越多,不停地在shared pool里面找chunk,然后不停地往library cache里面挂;
除了产生library cache的latch争用和shared pool的latch争用以外,还有一个问题:会产生大量的小chunk(小碎片),使得挂有小chunk的链越来越长,所以当再找空闲的chunk的时候,发现有好多空闲的空间,但是都是一些小的空闲空间,这时候,突然来了一个比较大的(比如包),就找不到合适的chunk,这时候oracle就会报一个错误:ORA-04031错误
这里我们可以认为:一个server process以S的方式在持有一个latch的时候,持有着latch,然后找到链,再找到chunk,在找到了chunk要读的时候,server process就释放了这个latch,因为读的时候只涉及到chunk,跟链没关系了(也就是在持有链的那一瞬间持有latch的)
ORA-04031错误
ORA-04031错误:
绝大部分情况是,要在shared pool里面找一个相对比较的大的chunk,但是随着时间的推移,会产生大量的小chunk(小碎片)就找不到合适的chunk,这时候就会报这个错误
对于oracle数据库来说,ORA-04031错误:
1、有一种情况是:它偶尔会报一次ORA-04031错误,这个对生产的影响不是很大;
2、另外一种情况是:ORA-04031错误会一直不停的报,这时候就要去解决这个问题了
解决ORA-04031错误最经典的一个例子:
1、看一下oracle数据库的硬解析,每秒是多少
2、如果硬解析数量很高的话,我们就要去library cache里面找常量SQL(就是没有使用绑定变量的SQL),然后将这些相关的常量SQL的字面量开发改成绑定变量
mutex
多个链用一个latch,这时候限制了并发,随着oracle数据库的并发越来越多,这时候就应该使用一个链一个latch,但是这种消耗内存比较多,oracle就采取另外一种措施:mutex, oracle从10g开始使用mutex(主要用在shared pool里面)
它跟latch的工作方式很相似,基本上就是一样,但是也有不同的地方(与latch的不同):
1、它是操作系统的内存结构,而latch是oracle数据库划分出来的一个内存结构
2、它的内存大小比latch小好多,小大约十倍
图解:
因为mutex很小,所以操作系统给每一个链申请一个mutex,因此比latch的并发要好一些,它跟latch在保护链的工作方式上是一样的
library cache mutex X(library cache latch的一个替代,11g里面新出现的):
在library cache里面,以X的方式产生一个mutex来保护链;library cache mutex X是要获取链
mutex可以取代latch,尤其是在shared pool里面
sub pool(子池)
图解:
9i以前的oracle,我们希望shared pool小于等于2G,oracle9i开始,shared pool大一些也没关系(比如10G),它把shared pool分成多个sub pool,每一个sub pool里面一套东西;但是shared pool的增长也是有问题的,所以shared pool的大小最好是一个比较稳定的值
library cache lock
图解:
0号链上挂着SQL和存储过程,1号链上也挂着一个存储过程,2号链上挂着一些表和视图,还有一些对象的定义信息,然后0号链上的存储过程依赖1号链上的存储过程,1号链上的存储过程又依赖2号链上的表和视图;
为什么还挂着一些对象的定义信息?假设要执行0号链上的存储过程,现在以S的方式持有着mutex,在链上找到这个存储过程以后,然后释放mutex;然后在这个存储过程上加一个lock,在1号链的存储过程上也加一个lock,在2号链的表和视图上也加上一个lock;
library cache里面为什么还会挂一些表和视图呢:它在上面挂的是一些表和视图的一些定义信息(视图的名字、视图的所有者...)
lock的作用:解决它们之间的依赖关系的(如果不加lack,在执行0号链上的存储过程的时候,有可能下面的两个被删掉了,0号链上的存储过程就执行不了了,oracle就要报错了)
lock是加在handle里面的
library cache pin
图解:
把0号链上的存储过程分解开,分解成一个handle(可以认为是存储过程的身份信息,handle在,标志着存储过程还在)和多个heap(堆)(存储过程编译后的编码什么的,存在heap里面,编码很大的话,需要多个heap,heap是可以随意申请的),存储过程的hash值、文本,存到handle里面去
handle在,标志着存储过程还在,但是handle在,不一定heap在(比如:在free里面找一个比较大的空闲的chunk,没找到,它就会到library cache里面找,根据需要,它就会将heap的空间释放掉一些,下次用的时候再加载进来就可以了,但是尽量不释放handle)
pin
要执行0号链上的存储过程的时候,在上面加一个lack,然后要执行后面的heap里面具体的代码,这时候,另外一个server process2在free里面找空闲的chunk,找不到了,就要释放heap,而这时候server process1正好在访问这个heap,但是server process2把它释放了,这时候就出现问题了;所以server process1在访问0号链上的存储过程的瞬间,在存储过程的handle上加上lock,在heap上加上pin,同时在handle上也加上一个pin,在handle上加上pin以后,下面的heap就都不能被释放
lock和pin在存储过程里面表现的比较突出
oracle非常害怕,在数据库之间出现严重的调用的问题,网状的或者循环的调用;当对里面其中一个对象重新编译以后,它们都需要重新编译,并且又有大量的进程进来要访问,就加lock,就锁在一起了,就会导致有大量的library cache lock,就会导致数据库hang死了,
cursor pin X
mutex还有一个作用:除了代替部分latch以外,还代替了部分pin锁
图解:
一个链上挂着SQL(父游标),下面有多个执行计划(子游标);父游标不容易被置换出去,但是子游标容易被age out出去;
现在要执行执行计划1,在执行的瞬间就要加一个pin锁;要执行执行计划1的时候,在它上面加一个mutex,修改它的时候也加一个mutex;
加一个mutex的好处:
假设,原来在执行的时候要加pin锁(实际pin是加在父游标上,现在是为了好理解加在了子游标上),执行完了,这个pin就销毁了,然后下次再执行的时候再加上pin锁;
现在呢,在它上面加一个mutex,mutex是一个内存结构,它里面有一个count(接收器);现在server process1要持有这个mutex,要来执行执行计划1,这个count就加上1;server process2来访问,也要加上1;server process3来访问的时候,发现count > 0,还有人在访问这个子游标,因为有人在访问这个子游标,它就没有被置换出去,server process2访问的时候就不需要再加pin了,server process3访问的时候也是不需要再加pin了;当访问完了:server process1访问完了,count就减去1,都访问完了,count就变成0了;