在前面的文章中,简单的介绍了oracle是如何在hash链表中搜索需要的数据块以及buffer header的过程,这里我们简单说明一下,如果没有在hash链表上找到所要的buffer header时,oracle会发生IO,到磁盘中去读取数据块,然后拷贝一份到buffer cache中的内存数据块里,后面具体的实现,是否有空闲cache,这里就不说明了,详细请看前面一篇文章。当然这里就要深入LRU和LRUW等链表结构了。
通过前面的文章,我们知道LRU链表是用来查找可重用的内存数据块块的,这里我们来看一下oracle是如何使用LRU链表的,以下已11G为准。
LRU链表和LRUW链表具有两个子链表,叫辅助链表和主链表,另外还针对buffer header增加了一个属性:touch数量(每个buffer header曾经被访问过的次数,用来对LRU链表进行管理),buffer header每访问一次,touch数量就会增加1,可以通过touch数量来反应某个内存数据块总共被访问的次数,但这是并不是精确的。
下面举一个简单的例子:假设buffer cache中只能容纳4个数据块,同时只有一个hash chain和一对LRU,当读入一个数据块时,该数据块对应的buffer header会挂到LRU辅助链表的最末端,同时touch数量+1,读取第二个不同的数据开时,第二块数据块对应的buffer header会挂到前一个buffer header的后面,从而第二块数据块位于LRU辅助链表的最末端,同时touch+1,以此类推,4个数据块全部用完以后的LRU链表可以用下图描述,每个buffer header的touch数量都为1.
LRU辅助链表:
BH1 touch:1(尾端)BH2 touch:1BH3 touch:1BH4 touch:1(首端)
此时主LRU链表是空的。BH1对应第一个查询数据块的buffer header,BH2对应第二个查询数据块的buffer header,BH3对应第三个查询数据块的buffer header,BH4对应第四个查询数据块的buffer header
由于LRU辅助链表只能容纳4个数据块,如果此时要求返回第5个数据块,这时oracle发现buffer cache里已经没有空闲的内存数据块了,于是就会从LRU辅助链表的尾端开始扫描,这里也就是从BH1处开始扫描,查找可以被代替的数据块,扫描的规则和可被代替数据块的规则如下:
1)如果被扫描到的buffer header的touch数量小于隐藏参数_db_aging_hot_criteria(该参数缺省为2)的值,则选中该buffer header作为牺牲者,并立即返回该buffer header所含有的数据块的地址。
2)如果当前buffer header的touch数量大于_db_aging_hot_criteria的值,则不会使用该buffer header。但是如果当前的_db_aging_stay_count的值小于_db_aging_hot_criteria的值,则会将当前该buffer header的touch值赋值给_db_aging_stay_count;否则将当前buffer header的touch数量减掉一半。
oracle总是从尾端开始扫描,那这里的BH1的touch数量为1,此时BH1会作为第一个牺牲者,对应的内存数据块的内容会被清空,同时将第5个数据块的内容拷贝进去,接下来发出第6个和第7个sql,第6个sql要求返回第5个数据块,第7个sql要求返回第4个数据块,也就是当前链表中的BH1和BH4,因为这2个数据块都在LRU辅助链表上,oracle会增加BH1和BH4的touch数量,同时将该BH1和BH4从LRU辅助链表上摘下,转移到LRU主链表的中间位置,我们用图下描述一下现在的LRU链表结构。
LRU辅助链表:BH1和BH4已经被摘下,BH2和BH3变成尾端和首端
BH2 touch1(尾端)BH3 touch:1(首端)
LRU主链表:
BH1 touch:2(尾端)BH4 touch:2(首端)
此时BH1对应第6个查询数据块的buffer header(同时也是第5个查询数据块的),BH2对应第二个查询数据块的buffer header,BH3对应第三个查询数据块的buffer header,BH4对应第7个查询数据块的buffer header,
如果此时又执行了第8句sql,要求返回第三个查询的数据块,也就是当前链表中的BH3,这时BH3会插入到LRU主链表上的BH1和BH4中间,并将BH3从LRU辅助链表摘下,这里要注意的是,每次向LRU主链表插入buffer header的时候,都是从中间插入。如果发来第9个sql,要求返回BH2,同样BH2会移入到LRU主链表的中间,此时主链表满了,LRU辅助链表空了,没有buffer header。
如果此时发来看第十句sql,要求返回一个全新的数据块,oracle就会先扫描LRU辅助链表,但上面没有任何buffer header,然后会扫描LRU主链表,从尾部开始扫描,采用前面所说的牺牲规则来挑选牺牲者,挑出的可以被替代的buffer header将从LRU主链表摘下,放入LRU辅助链表中。
如上就是oracle管理LRU链表的方式,可以从管理方式中看出,oracle总是希望保留多次被访问的数据块在内存中,同时又考虑到了有限的内存资源,这样达到了一箭双雕的效果。
转载请注明: 版权所有,文章允许转载,但必须以链接方式注明源地址,否则追究法律责任!
最后编辑:2014-01-09作者:Jerry
一个积极向上的小青年,热衷于分享--Focus on DB,BI,ETL