首页 > Oracle > Oracle DBWR进程的工作流程以及和其他进程的协调工作!
2014
01-16

Oracle DBWR进程的工作流程以及和其他进程的协调工作!

前面简单介绍了DBWR进程的触发条件,但实际上写入数据文件是一个非常复杂的过程。而且在这个过程中,需要考虑诸多因素,比如数据的安全,还有数据的写入效率。数据安全,就是在数据写入的过程中,一旦数据库崩溃或者实例异常,那这些数据就需要得到保护,保证用户已经提交的数据不会丢失,其次一般在数据库中,IO是比较大的瓶颈,而且也是非常昂贵的操作,一般好的存储,无论是前期投入还是后期维护,成本还是很大的。所以oracle会尽可能的将脏数据收集到一定程度后,再批量写入到磁盘。

当然oracle也可以不批量写入,每当用户提交的时候就将脏数据块提交给DBWR,然后写入数据文件,这样虽然能报保证提交的数据不会丢失,但一旦处于一个高并发的系统,一定会引起IO争用,这就得不偿失了。为了提供一个既安全又效率的写入数据块的办法,oracle使用了CKPT和LGWR这两个后台进程进行协作。

LGWR大家应该都很熟悉,也就是我们经常提到的redo,当用户进程每次修改内存数据块时,对会在日志缓冲区中够着一个相应的重做日志条目redo entry,该条目描述了被修改的数据块在修改之前和修改之后的值,而LGWR进行就是用来将这些重做条目写入到联机日志文件redo  log中,一旦redo log写入到了联机日志文件中,那数据的安全就得到了保障。LGWR虽然是一个后台进程,但这个进程实时和前台用户进行通信,承担系统数据完整性的任务,保证我们的数据在任何情况下不丢失。

LGWR进程写入联机日志文件的方式也有两种:后台写(background write)和同步写(sync write),两种方式的触发条件不一致:

触发同步写的条件:

1)每隔三秒钟,LGWR启动一次;

2)在DBWR启动时,如果发现脏数据块所对应的重做条目还没有写入联机日志文件,则DBWR触发LGWR进程并等待LRWR写完以后才会继续;

3)重做条目的数量达到整个日志缓冲区的1/3时,触发LGWR;

4)重做条目的数量达到1MB时,触发LGWR。

而触发同步写的条件就只有一个:当用户提交commit时,触发LGWR。

当DBWR进程在写数据块的过程中,发生实例崩溃,因为用户提交的时候,LGWR已经将数据写入到联机日志文件,但是可能还没有把数据写入到数据文件,此时实例崩溃,必然会有一部分已经提交但是还没有来得及写入到数据文件的内存数据块丢失了。当实例再次启动的时候,oracle会利用重做日志文件中的记录在buffer cache中重新构造被丢失的数据块,以便完成前滚和回滚,将丢失的数据块找回。这里就遇到一个问题,oracle是怎么知道应该从哪里开始应用重做日志,应该从哪里开始找?

oracle当然不会笨到扫描所有的重做日志文件,oracle需要随时预防实例崩溃的可能,所有在数据库运行过程中,oracle必须不断定位这个重做的起点位置,以便在下一次实例崩溃的时候,能够快速定位并应用重做日志条目。那这个起点位置如何定义?首先这个起点不能太靠前,这样意味着要处理更多的重做条目,这样会导致实例再次启动的时候所进行的恢复的时间更长,其次起点也不能太靠后,太靠后说明只有很少的脏数据块没有被写入数据文件,也就是说前面已经有很多脏数据块被写入了数据文件,那也就意味着只有在DBWR启动的很频繁的情况下,才能使得buffer cache中所残留的脏数据块的数量很少。但是DBWR启动频繁的话,那么系统IO压力就会越大,留给其他操作的IO资源就更少。这样显然是不靠谱的。

所以这个起点的定义还是非常重要的,为了能更加准确的定位这个起点,oracle就引入了CKPT的后台进程,通常称之为检查点进程checkpoint process。这个进程和DBWR共同协作来定位这个起点,这个起点还有另一个名字:检查点位置checkpoint position。oracle为了在检查点的算法上具有可扩展性,还引入了检查点队列checkpoint queue,该队列都是脏数据块所对应的buffer header,只要在检查点的队列上的数据块都是还没有写入数据文件的脏数据块,除了检查点队列之外,还有文件队列,这是为了更加有效的处理单实例和多实例环境下的表空间的检查点处理,比如将表空间设置为离线状态或者为热备份状态等,文件队列的原理与检查点队列的原理是一样的,每个数据文件会有一个队列,且该数据文件所对应的脏数据块串在同一个队列上,当然oracle不会每次都生成一个全量检查点,还有增量检查点,来增加检查点启动的次数。

上面说到和CKPT有关的两个概念:检查点队列和增量检查点,检查点队列在前面文章大家应该看见过,在转储出来的buffer header里就可以看到,有关键字ckptq和fileq的结构,记录的是指向前一个buffer header和后一个buffer header的指针,这个队列上挂载的也是脏数据块对应的buffer header,但是此处与前文说的LRUW链表不同。检查点队列上的buffer header是按照数据块第一次被修改的时间的先后顺序来排列的,越早修改的数据块的buffer header排在最前面,如果一个数据块被修改了多次的话,在该链表中也只出现一次。而且在检查点队列上的buffer header还记录了脏数据块在第一次被修改的时候,所对应的重做条目在重做日志文件中的地址,也就是RBA:redo block address。同样可以从转储文件中找到LRBA的关键字,L表示LOW,也就是第一次被修改的时候的RBA,但是要注意的是,在检查点队列上的buffer header,并不表示会有一个对应的RBA,比如控制文件重做就不会有,对于这种没有对应RBA的buffer header来说,在检查点队列上始终处于最尾端,其优先级永远比有RBA的脏数据块的buffer header要低。每个working set 都有两个检查点队列,每个检查点都会由checkpoint queue latch 保护。

完全检查点只有在两种情况才会触发:1. 发出命令:alter system checkpoint; 2. 除了shutdown abort以外的正常关闭数据库。这个时候,日志切换不会触发完全检查点,只会触发增量检查点。增量检查点是8I开始引入的,每隔三秒钟或者发生日志切换时启动,启动的时候会找出当前检查点队列上的第一个buffer header,并将该buffer header中所记录的LRBA(也就是checkpoint position)记录到控制文件中,此外如果增量检查点是由日志切换发生的,还会将checkpoint position记录到每个数据文件头中,也就是说,如果此时发生实例崩溃,oracle在下次启动的时候,就会到控制文件中找到这个checkpoint position作为在日志文件中的起点,然后从这个起点开始向后依次取出每个重做条目进行处理。

总结:DBWR负责写检查点队列上的脏数据块,CKPT负责记录当前检查点队列的第一个数据块所对应的重做条目在日志文件中的地址。而前文提到是LRUW主要是区分哪些数据块是脏数据块,不可以被重用,而到底应该写哪些脏数据块,写多少脏数据块,DBWR还是需要到检查点队列上才能确定。

最后编辑:
作者:Jerry
一个积极向上的小青年,热衷于分享--Focus on DB,BI,ETL