+++++buffer cache 深度解析( 五 )


在 cache中,最耳熟能详的链表可能就是LRU链表了 。在前面描述 cache结构的图上,也可以看到有两个链表:LRU和LRUW 。在介绍LRU和LRUW前,先说明几个概念 。
1) 脏数据块(dirty ): cache中的内存数据块的内容与数据文件中的数据块的内容不一致 。
2) 可用数据块(free ): cache中的内存数据块为空或者其内容与数据文件中的一致 。注意,可用数据块不一定是空的 。
3) 钉住的数据块(ping ):当前正在更新的内存数据块 。
4) 数据库写进程(DBWR):这是一个很底层的数据库后台进程 。既然是后台进程,就表示该进程是不能被用户调用的 。由内置的一些事件根据需要启动该进程,该进程用来将脏数据块写入磁盘上的数据文件 。
LRU表示LeastUsed,也就是指最近最少使用的 链表 。LRU链表串连起来的 都指向可用数据块 。而LRUW则表示LeastUsed Write,也叫做dirty list,也就是脏数据块链表,LRUW串起来的都是修改过但是还没有写入数据文件的内存数据块所对应的。某个 要么挂在LRU上,要么挂在LRUW上,不能同时挂在这两个链表上 。
随着硬件技术的发展,电脑的内存越来越大 。cache也是越来越大,只用一条LRU和一条LRUW来管理 已经不够用了 。同时还引入了多个DBWR后台进程来帮助将 cache中的脏数据块写入数据文件,显然,多个DBWR后台进程都去扫描相同的LRUW链表会引起争用 。为此引入了 set(工作集)的概念 。每个 set都具有它自己的一组LRU和LRUW链表 。每个 set都由一个名为“cachelru chain”的latch(也叫做lru latch)来管理,所以从这个意义上说,每一个lru latch就是一个 set 。而每个被加载到 cache的 都以轮询的方式挂到 set上去 。也就是说,当 cache加载一个新的数据块时,其对应的 会去找一个可用的lru latch(找这个工作集中的lru列表,将新加载进来的数据块挂到LRU列表上),如果没有找到,则再找下一个lru latch,直到找到为止 。如果轮询完所有的lru latch也没能找到可用的lru latch,该进程只有等待latch free等待事件,同时出现在v$中,并增加“latch ” 。如果启用了多个DBWR后台进程的话,每个DBWR进程都会对应一个不同的 set,而且每个DBWR只会处理分配给它的 set,不会处理其他的 set 。
我们已经知道一个lru latch就是一个 set,那么 set的数量也就是lru latch的数量 。而lru latch的数量是由一个隐藏参数:s决定的 。该参数缺省值为DBWR进程的数量×8 。
该参数最小必须为8,如果强行设置比8小的数值,将忽略你设置的值,而使用8作为该参数值 。
1SQL> alter system set "_db_block_lru_latches"=1 scope=spfile;2SQL> startup force3SQL> show parameter _db_block4NAMETYPEVALUE5------------------------------------ ----------- ------------------------------6_db_block_lru_latchesinteger8
3.2.2 深入LRU链表
我们已经知道LRU链表是用来查找可以重用的内存数据块的,那么是怎么使用LRU链表的呢?这里需要分为8i之前和8i以后两种情况 。
在8i之前,我们举一个例子 。假设 cache只能容纳4个数据块,同时只有一个hash chain和一个LRU 。当数据库刚刚启动, cache是空的 。这时前台进程发出语句获取数据块时,找一个空的内存数据块,并将其对应的 挂到hash chain上 。同时,还会把该 挂到LRU的最尾端 。随后前台进程又发出语句,这时所找到的 在LRU上会挂到前一个 的后面,也就是说第二次语句所找到的 现在变成了LRU的最尾端了 。假设发出4句以后找到了4个 ,从而用完了所有的 cache空间 。这个时候的LRU可以用下图二来表示 。
这个时候,发来了第五句语句 。这时的 cache里已经没有空的内存数据块了 。但是既然需要容纳下第五个数据块,就必然需要找一个可以被替换(后面会看到类似牺牲、重用的字样,它们和替换都是一个意思)的内存数据块 。这个内存数据块会到LRU上去找 。按照设定的最近最少使用的原则,位于LRU最尾端的BH1将成为牺牲者,会把该BH1对应的内存数据块的内容清空,并将当前第五句SQL所获得的数据块的内容拷贝进去 。这个时候,BH1就成了LRU的首端,而BH2则成为了LRU的尾端 。如下图三所示 。在这种方式下,经常被访问的数据块可以一直靠近LRU的首端,也就保证了这些数据块可以尽可能的不被替换掉,从而保证了访问的效率 。