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


3.1 在 cache中获取所需要的数据块的过程
当前台进程发出或者其他DML语句时,根据SQL语句的执行计划所找到的数据块(根据查询索引或者全表扫描,找到要查询的数据条目所在的块,一般根据ROWID等信息,举个列子:一个简单的语句,条件是empno=7788,根据索引,会知道7788这个条目的ROWID,然后就能知道数据块的位置,到底是在那个表空间,那个对象,那个数据文件中),会构造一个名为数据块描述( )的内存结构 。该 位于的PGA中,所包含的内容主要是数据块所在的物理地址(根据ROWID信息的第33-64bit构造出rbda)、数据块的类型、数据块所属对象的 id等信息 。
随后,会把对数据块请求的锁定模式以及所构造出来的 传入专门搜索数据块的函数中 。在该函数中,根据 所记录的信息,应用hash算法以后,得到要找的数据块所处的hash ,也就是确定该数据块在哪条hash chain上 。然后,进入该hash chain,从上面所挂的第一个 开始搜索,一直搜索到最后一个。
在hash chain上搜索的逻辑如下:
1) 比较 上所记录的数据块的地址(rdba),如果不符合,则跳过该。
2) 跳过状态为CR的。(说明有别的进程正在进行一致性读,所以才构造了这个cr块,如果我也要找这个块的原块,我需要自己再重新构造一个新的cr块,不会使用这个旧的cr块,如果我不是找这个块的原块,那我不需要构造,所以这两种情况下都是跳过cr块)
3) 如果遇到状态为(正在从磁盘上读出的数据块)的 ,则等待,一直等到该 的状态改变以后再比较所记录的数据块的地址是否符合 。(说不定是之前的查询,有可能就是这条sql语句,也有可能是之前的(自己用户或者其他用户的sql)语句,正好也需要读这个块内的数据,正在往内存里读,这下我就可以直接用前辈的努力就可以了)
4) 如果发现数据块地址符合的 ,则查看该 是否位于正在使用的列表上,如果是位于正在使用的列表上,则判断已存在的锁定模式与当前所要求的锁定模式是否兼容,如果是兼容的,则返回该 所记录的数据块地址,并将当前进程号放入该 所处的正在使用的列表上(在开始介绍 的时候有这一项,还有等待该 的进程列表) 。
5) 如果发现锁定模式不兼容,则根据找到的 所指向的数据块的内容,构建一个新的、内容一样的、数据块状态为(实例以排他方式获取的当前模式数据块)的复制数据块,并且构造一个状态为CR的 ,同时该 指向所新建立的复制数据块 。然后,返回该复制数据块的地址,并将当前进程号放入该 所处的正在使用的列表上 。
6) 如果比较完整个hash chain以后还没发现所要找的 ,则从磁盘上读取数据文件 。并将读取到的数据块所对应的 挂到hash chain上 。(之前我有个疑问,到底是在生成执行计划的时候就开始往内存里读取数据块?还是在用比较地址的时候读?现在知道了,是在用比较地址的时候往里读取的)
3.2 LRU和LRUW链表结构及其管理机制
3.2.1 LRU和LRUW链表结构概述

+++++buffer cache 深度解析

文章插图
在前面,我们已经知道了是如何在hash chain中搜索要找的数据块所对应的 的过程,我们也知道如果在hash chain上没有找到所要的 时,会发出I/O调用,到磁盘上的数据文件中获取数据块,并将该数据块的内容拷贝一份到 cache中的内存数据块里(顺带提一句,内存数据块通常叫做,而数据文件里的数据块通常叫做block,二者是一个意思) 。这个时候,假如 cache是空的,比较好办,直接拿一个空的内存数据块来用即可 。但是如果 cache中的内存数据块全都被用掉了,没有空的内存数据块了,怎么办?应该重新使用哪一个内存数据块?当然我们可以一个一个的比较内存数据块与其对应在数据文件中的数据块的内容是否一致,如果一致则可以将该数据块拿来,将其内容清空,然后拷贝上当前数据块的内容;如果不一致,则跳过,再找下一个 。毫无疑问,这种方式效率低下 。为了高效的管理 cache中的内存数据块,引入了LRU和LRUW等链表等结构 。