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


同时为了能够尽量减少实例崩溃后恢复的时间,还引入了增量检查点( ),从而增加了检查点启动的次数 。如果每次检查点启动的间隔时间过长的话,再加上内存很大,可能会使得恢复的时间过长 。因为前一次检查点启动以后,标识出了这个起点 。然后在第二次检查点启动的过程中,DBWR可能已经将很多脏数据块已经写入了数据文件,而假如在第二次检查点启动之前发生实例崩溃,导致在日志文件中,所标识的起点仍然是上一次检查点启动时所标识的,导致不知道这个起点以后的很多重做条目所对应的脏数据块实际上已经写入了数据文件,(我的理解:前滚恢复到日志的最后一条重做条目,发现没有检查点,接着再回滚,回到上一个检查点对应的日志条目)从而使得在实例恢复时再次重复的处理一遍,效率低下,浪费时间 。
上面说到了有关CKPT的两个重要的概念:检查点队列(包括文件队列)和增量检查点 。
检查点队列在我们上面转储出来的 里可以看到,就是类似ckptq: [,]和fileq: [,]的结构,记录的同样都是指向前一个 和指向后一个 的指针 。这个队列上面挂的也是脏数据块对应的 链表,但是它与LRUW链表不同 。检查点队列上的 是按照数据块第一次被修改的时间的先后顺序来排列的 。越早修改的数据块的 排在越前面,同时如果一个数据块被修改了多次的话,在该链表上也只出现一次 。而且,检查点队列上的 还记录了脏数据块在第一次被修改时,所对应的重做条目在重做日志文件中的地址,也就是RBA(Redo Block ) 。同样在转储出来的 中可以看到类似LRBA: [0xe9.229.0]的结构,这就是RBA,L表示Low,也就是第一次被修改的时候的RBA 。但是注意,在检查点队列上的 ,并不表示一定会有一个对应的RBA,比如控制文件重做( redo)就不会有相应的RBA 。对于没有对应RBA的 来说,在检查点队列上始终处于最尾端,其优先级永远比有RBA的脏数据块的 要低 。8i以前,每个 set都有一个检查点队列以及多个文件队列(因为一个数据文件对应一个文件队列);而从8i开始,每个 set都有两个检查点队列,每个检查点都会由 queue latch来保护 。
而增量检查点是从8i开始出现的,是相对于8i之前的完全检查点( )而言的 。完全检查点启动时,会标识出 cache中所有的脏数据块,然后启动DBWR进程将这些脏数据块写入数据文件 。8i之前,日志切换的时候会触发完全检查点 。而到了8i及以后,完全检查点只有在两种情况下才会被触发:
1)发出命令:alter;
2)除了 abort以外的正常关闭数据库 。
注意,这个时候,日志切换不会触发完全检查点,而是触发增量检查点 。8i所引入的增量检查点每隔三秒钟或发生日志切换时启动 。它启动时只做一件事情:找出当前检查点队列上的第一个 ,并将该 中所记录的LRBA(这个LRBA也就是 了)记录到控制文件中去 。如果是由日志切换所引起的增量检查点,则还会将 记录到每个数据文件头中 。也就是说,如果这个时候发生实例崩溃,在下次启动时,就会到控制文件中找到这个 作为在日志文件中的起点,然后从这个起点开始向后,依次取出每个重做条目进行处理 。
上面所描述的概念,用一句话来概括,其实就是DBWR负责写检查点队列上的脏数据块,而CKPT负责记录当前检查点队列的第一个数据块所对应的的重做条目在日志文件中的地址 。
从这个意义上说,检查点队列比LRUW还要重要,LRUW主要就是区分出哪些数据块是脏的,不可以被重用的 。而到底应该写哪些脏数据块,写多少脏数据块,则还是要到检查点队列上才能确定的 。