Buffer Cache概述

众所周知,读取磁盘的速度相对来说是非常慢的,而读取内存的速度相对则要快得多。因此为了能够加快处理数据的速度,Oracle必须将读取过的数据缓存在内存里。而Oracle对这些缓存在内存里的数据起了个名字:数据块缓存区(Database buffer cache),通常就叫做buffer cache。按照Oracle官方的说法,buffer cache就是一块含有许多数据块的内存区域,而这些数据块主要都是数据文件中数据块内容的副本。通过初始化参数buffer_cache_size来指定buffer cache的大小。Oracle实例一旦启动,该区域大小就被分配好了。

buffer cache所能提供的功能主要包括:

通过缓存数据块,从而减少I/O。

通过构造CR(Consistent Read)块,从而提供读一致性功能。

通过提供各种lock、latch机制,从而提供多个进程并发访问同一个数据块的功能。

内存里的数据块通常叫做buffer,而数据文件里的数据块通常叫做block,二者是一个意思。一般我们会混用这两个名词。

Buffer Cache的内存结构

Oracle内部在实现其管理的过程中,有两个非常有名的名词:链表和hash算法。

链表是一种数据结构,通过将对象串联在一起,从而构成链表结构。这样,如果要修改、删除、查找某个对象的话,都可以先到链表中去查找,而不必实际地访问物理介质。

从上图我们可以看到,buffer cache就像一个水池,水池的最小单位就是数据块。当每个数据块被读入buffer cache时,Oracle都会抽取数据块的头部,在内存中构建buffer header,并将这些buffer header串成链表的形式。而buffer header里面记录的指针就指向buffer cache中的该数据块本身。于是,Oracle在搜索某个数据块时,就不用去buffer cache中找,而是直接扫描链表上该数据块所对应的buffer header,然后根据找到的buffer header所记录的指针就能到buffer cache中直接定位该数据块了。

在管理buffer header的过程中,Oracle同样借助了hash算法。通过对buffer header里记录的数据块地址和数据块类型运用hash函数以后,得到该数据块所属的组号。这里的组号就是图的hash bucket。

这里的hash chain就是属于同一个hash bucket的所有buffer header所串起来的链表。实际上,hash bucket只是一个逻辑上的概念。每个hash bucket都是通过不同的hash chain体现出来的。每个hash chain都会由一个cache buffers chains latch来管理其并发操作。

启动数据库以后,Oracle究竟产生多少个hash bucket,则由Oracle自己计算。

当前台进程发出SELECT或者其他DML语句时,Oracle根据SQL语句的执行计划找到符合SQL条件的数据块,然后Oracle会根据对请求的数据块的地址以及数据块的类型作为参数,应用hash函数以后,得到要找的数据块所处的hash bucket,也就是确定该数据块在哪条hash chain上。然后,Oracle进入该hash chain,从上面所挂的第一个buffer header开始,根据buffer header所含有的指针找到对应的块体,然后扫描其中的数据,确认其是否是SQL语句所需要的块,如果是,则返回该块里所需要的数据;否则,如果不是,则继续往下搜索,一直搜索到最后一个buffer header为止。如果一直都没有找到,则调用物理I/O,到数据文件里把该块所含有的内容复制一份到一个可用的buffer里,并构建该块的buffer header,然后将该buffer header挂到hash chain上去。

Buffer Cache的管理机制
我们知道如果在hash chain上没有找到所要的buffer header时,Oracle会发出I/O调用,到磁盘上的数据文件中获取数据块,并将该数据块的内容复制一份到buffer cache中的内存数据块里。这个时候,假如buffer cache是空的,比较好办,直接拿一个空的内存数据块来用即可。但是如果buffer cache中的内存数据块全都被用掉了,没有空的内存数据块了,怎么办?应该重新使用哪一个内存数据块?当然我们可以逐个比较内存数据块与其对应在数据文件中的数据块的内容是否一致,如果一致则可以将该数据块拿来,将其内容清空,然后将当前数据块的内容复制进入;如果不一致,则说明数据块在内存里被修改了,但是还没有写入数据文件,因此该数据块不能被其他内容覆盖,则跳过,再找下一个。毫无疑问,这种方式效率低下。为了高效地管理buffer cache中的内存数据块,Oracle引入了LRU链表等结构。
在介绍LRU前,先说明几个概念。
脏数据块(dirty buffer):buffer cache中的内存数据块的内容被修改,从而导致与数据文件中的数据块的内容不一致。
空闲数据块(free buffer):buffer cache中的内存数据块为空。
干净数据块(clean buffer):buffer的内容与数据文件中的一致。
钉住的数据块(pin buffer):当前正在更新的内存数据块。
数据库写进程(DBWR):这是一个很底层的数据库后台进程。既然是后台进程,就表示该进程是不能被用户调用的。由Oracle内置的一些事件根据需要启动该进程,该进程用来将脏数据块写入磁盘上的数据文件。
对于空闲数据块和干净数据块,我们一般都统称为可用数据块,因为其中的内容可以被新的数据内容覆盖。其他状态的数据块,比如脏数据块,则不能被新的内容覆盖。
LRU表示Least Recently Used,也就是指最近最少使用的buffer header链表。LRU链表串联起来的buffer header都指向可用数据块。buffer按照被使用的先后顺序挂在LRU链表上,先被使用的buffer挂在LRU链表的最近最少使用端,后被使用的buffer则被挂在LRU链表的最近最多使用端。如果buffer被DML语句修改了,则该buffer会从 LRU链表的最近最少使用端寻找第一块空闲的内存 。换句话说,常用的数据总是在内存中,不常用的数据会被写到磁盘中。
当服务器进程无法找到空的buffer来存放新的数据请求时,则需要把已经存放了数据的buffer拿来使用,也就是用新的数据块的内容覆盖曾经使用过的buffer。在查找应该覆盖哪个buffer时,Oracle会在LRU链表上的最近最少端开始扫描,如果扫描到的buffer正在被使用,则跳过该buffer,继续往下找,直到找到为止。如果扫描了一定数量的buffer以后还没找到可用的buffer,则说明脏块太多了,于是触发DBWn进程,将脏块刷新到数据文件里,刷新完毕以后,buffer的内容与数据文件里的一致,于是这些脏块就变成干净的buffer了,也就可以拿来覆盖其中的内容了。这些干净的buffer就会挂在LRU链表的尾部,供进程所使用。
当进程在LRU链表上扫描可用数据块时,会受到cache buffers lru chain latch的保护。

这里又延伸出了另一个问题,每次DBWn进程要从缓冲区中写多少数据到磁盘中?这个过程的机制是什么?

这里大家需要另一个缓冲区列表,检查点队列。检查点队列是一个等待通过DBWn进程写至磁盘的 缓冲区列表,这个列表由搜索空闲缓冲区的服务器进程进行填充。服务器进程会从LRU 表的最近最少使用端开始查找空闲缓冲区,只要在查找期间发现一个脏缓冲区,服务器进 就会将这个脏缓冲区的地址移动至检查点队列。因此,检查点队列是一个由并非最近使用 脏缓冲区组成的列表。有时,DBWn进程会将检查点队列的所有缓冲区都复制至磁盘, 而使这些缓冲区被清空 ( 或者空闲 ) 以便重用 。 这意味着非常繁忙的缓冲区 ( 例如一个不断被 新的缓冲区 ) 永远不会被重写至磁盘,其原因在于这种缓冲区始终位于 LRU列表的最近最 使用端,从而不可能被查找空闲缓冲区的服务器进程发现。这种非常简单的机制允许 ORACLE保持最低的磁盘 I/O:DBWn 进程只将静态的和脏的数据写至磁盘。未变化的缓冲区 不会被写至磁盘,而脏缓冲区只有在其未被访问的时间达到指定值时才会被写至磁盘。
在正常运行时,DBWn 进程会在两种情况下清空检查点队列。

第一种:服务器进程查找空闲缓冲区耗费的时间可能过长 (“过长”的程度由内部参数控制 ),此时就会通知 DBWn 进程将检查点队列中的缓冲区写至磁盘。第二种情况是检查点队列可能变得过长, 此时任何服务器进程都不存在问题,但是查找空闲缓冲区的服务器进程已经发现了大量的脏 缓冲区。不管是在哪一种情况下,只要DBWn进程将检查点队列中的脏缓冲区复制至磁盘, 脏缓冲区就会变为干净缓冲区,服务器进程随后就可以使用这些干净缓冲区。如果数据库是 静态的,那么就不会使用查找空闲缓冲区的机制,此时需要使用另一种机制。DBWn进程 具有每隔3秒钟的暂停时间:无论查找空闲缓冲区的服务器进程是否已发现脏缓冲区, DBWn进程每隔3秒钟就会将一些脏缓冲区写至磁盘。DBWn进程会遍历LRU列表,然后 将最近没有使用的前几个脏缓冲区写至磁盘。以致整个数据库高速缓存区最终被清空。
上述算法的最后结果是:DBWn进程向磁盘写入尽可能少的数据,并且相对静态的数 据的写操作总是优先于不稳定数据的写操作。只有在发生数据库检查点进程时,所有缓冲区 才会通过一个操作被写至磁盘。上述操作既可以通过有序关闭数据库进行,也可以在发出 ALTER SYSTEM CHECKPOINT 命令时执行。下列行为将进行表空间检查点,使得该表空间下的所有脏缓冲区都被写至磁 盘:
删除表空间
使表空间只读
将表空间置入热备份模式
将表空间脱机

 

感谢$无为公子的帮助

参考至:《教你成为10g OCP》韩思捷著

如有错误,欢迎指正