原文: DBA的思想天空- p40
在 DB Cache的算法中,数据库启动之初,空闲的 Cache都是在LRU链上的。前台进程需要分配 DB Cache 的时候,从 LRU链的冷端开始扫描,查找可用的
Cache。找到 Cache后,根据要读入的数据块的 RDBA,通过散列算法找到一个HASH链,将这个数据块头上的 HASH链相关指针链接到 HASH链上。
前台进程只负责将数据块从文件中读取到DBCache中,而DBCache中的脏数据是由专门的后台进程来负责写入数据文件的,这个后台进程就是DBWR。在介绍详细的算法之前,我们首先需要学习几个数据库的统计数据。这几个统计数据对于我们理解 DB Cache 的算法十分重要。由于 DB Cache的算法改进十分频繁,因此本节介绍的一些算法,都是基于8i版本的,9i或者更高的版本中,算法的核心并未改变,只是在细节上有所不同,请大家阅读本节时注意
首先我们来看看
Foreground scan depth这个参数,它和前台进程搜索 DB Cache有关。前台进程要分配 DB Cache的时候,会从LRU的尾部开始搜索,如果发现了可用的 DB Cache就 PIN住使用。如果找到的这个
Cache目前是脏的,还没写盘或者被其他会话PIN住,或者被标识为热块,那么这个 Cache 就不能使用,需要继续向前搜索。
而这种搜索工作有可能进行很长时间都无法找到可用的 Cache,这样就极大地影响了LRU链的性能,因此需要定义一个最大深度,一旦超过这个搜索深度,就放弃搜索,同时记录一个标志位,通知 DBWR 刷新脏数据到磁盘。同时前台进程进人休眠状态,等待 free buffer的消息,收到该消息后重新开始査找工作。这个深度默认设置为 DBCache的 1/4,并可以通过参数_db_block_max_scan_pct来调整(在每个版本中,该参数的初始值都会有所不同)。
在搜索可用Cache的时候,如果发现某个BUFFER是脏的,那么前台进程就会马上将该BUFFER从 LRU链上摘下,放到LRU-W的尾部,并且增加
buffers moved to the dirty queue by foregrounds 统计量。如果发现 LRU-W链的长度已经大于 Max
dirty queue,那么前台进程也会停止搜索操作,并记录一个标志位,通知 DBWR将脏数据写到磁盘上。同时前台进程进入休眠状态,等待 DBWR完成写入。
这种情况下,如果known
clean bufers计数器的值不是0,那么就会减少 kmown clean buffers的计数。known clean bufers 是一个计数器,该计数器记录的是当前的DB Cache中可能的干净块(也就是非脏的块)的数量。这个计数器在 DBCache 相关算法中还有些其他的用处,等老白慢慢介绍。Max dirty
queue的大小是 Max write
batch的两倍,Max
writebatch是一次写批处理操作的最大数量,可以通过参数_db_block_write_batch 来调整。
如果前台进程在搜索中找到了可用的 BUFFER,那么它就会将该 BUFFER移动到 LRU链热端的尾部,同时减少 known clean buffers 的计数。如果 known clean buffers 计数小于 dbwr scan depth的一半,那么前台进程也会设置标识,触发 DBWR写操作。一般情况下,dbwr scan depth 初始化时的值等于 min scan depth(默认为 DB Cache的 1/4),不过这个参数会在数据库运行过程中动态调整。
上面所说的算法并不能完全反映
Oracle 的真实算法,不过大体上能够体现其主要思想。当DBCache中可用的空闲块不足的时候,会设置 DBWR Make Free Message,该消息会唤醒休眠的DBWR进程,并开始工作。当这个消息被 DBWR收到后会发生什么呢?
DBWR首先会获取LRU闩锁,清除DBWR Make
Free Message标志,然后会检查 LRU-W链。如果 LRU-W是空的,并且known free bufers 计数大于 dbwr scan depth的一半,那么 DBWR什么都不做,直接释放闩锁,然后继续“睡觉”。
如果 DBWR发现LRU-W上的BUFFER数量不够一-次 DBWR BATCH操作,那么 DBWR就会从 LRU 的尾部开始搜索脏数据块,发现后,将这个数据块移动到LRU-W 的尾部。如果扫描的数据块没有被移动到LRU-W,那么clean buffers scanned 计数器会增加。搜索结束的条件是LRU-W上的数据块数量达到了 dbwr batch 的数量,或者搜索的深度达到了 dbwr scan depth 的值(该参数在数据库运行过程中会自动调整,调整范围在min scan depth和 max scan depth之间 )。
扫描结束后,known
clean buffer 的值被重置为 DBWR扫描后的 clean buffers scanned 的值与本次dbwr write batch写入的缓冲区数的总和。此时,LRU闩锁被释放,然后 DBWR将脏数据块写入数据文件,并将已经写入的缓冲区放入 LRU-AUX链。
如果 DBWR写入完成后,LRU-W链空了,或者kown clean buffer count已经大于 dbwr scan depth 的 3/4,那么 dbwr scan depth将被减少一个增量。因为系统认为 DBWR 的写操作总量可以略微降低。如果写入后,known clean buffer count还小于dbwr scan depth的1/2,那么dbwr scan
depth会增加一个增量,因为系统认为DBWR的写操作总量需要增加。
除了 Make Free
Message外,还有其他的消息可以唤醒DBWR,最为典型的是 Checkpoint消息和 Reuse Block Range消息。Reuse Block Range 可能由 drop、truncate等 ddl命令发出。此外每隔3秒钟,会产生一个 DBWR
timeout事件,如果 DBWR发现休眠超时后,LRU-W还是空的那么它就会以两倍于 dbwr scan depth 的深度搜索 LRU-W,寻找脏数据块。