分类: Oracle
2008-04-28 09:37:14
一、检查点概述
大多数关系型数据库都采用“在提交时并不强迫针对数据块的修改完成”而是“提交时保证修改记录(以重做日志的形式)写入日志文件”的机制,来获得性能的优势。这句话的另外一种描述是:当用户提交事务,写数据文件是“异步”的,写日志文件是“同步”的。这就可能导致数据库实例崩溃时,内存中的DB_Buffer 中的修改过的数据,可能没有写入到数据块中。数据库在重新打开时,需要进行恢复,来恢复DB Buffer 中的数据状态,并确保已经提交的数据被写入到数据块中。检查点是这个过程中的重要机制,通过它来确定,恢复时哪些重做日志应该被扫描并应用于恢复。
要了解这个检查点,首先要知道checkpoint queu概念,检查点发生后,触发dbwn,CKPT获取发生检查点时对应的SCN,通知DBWn要写到这个SCN为止,
dbwr 写dirty buffer 是根据 buffer 在被首次 modify的时候的时间的顺序写出,也就是 buffer被modify 的时候会进入一个queue (checkpoint queue),dbwr 就根据queue从其中批量地写到数据文件。 由于这里有一个顺序的关系,所以 dbwr的写的进度就是可衡量的,写到哪个buffer的时候该buffer的首次变化时候的scn就是当前所有数据文件block的最新scn,但是由于无法适时的将dbwr的进度记录下来,所以oracle 选择了一些策略。 其中就包括ckpt进程的检查点和心跳。
但oracle考虑到检查点scn的间隔还是太大了,因为检查点的触发条件有限,周期可能比较长,有些情况下比如检查点需要5分钟才触发,那这个时候系统crash 再重新启动就意味着很可能系统需要5分钟才能启动。
于是oracle 采用了一个 心跳的概念,以3秒的频率将 dbwr 写的进度反应到控制文件中,这样系统crash 重新启动的时候将从更近的一个 时间点开始恢复。
再这里同样需要说明的一点是dbwr 并不是只有当 检查点发生的时候才写,它大约有10几种条件触发写操作
所以这个问题,我们需要理解的是oracle为什么要这么做?
oracle的目的就是缩短崩溃恢复时间!
oracle 如何缩短恢复时间?
1: 检查点机制
2: 心跳机制
oracle 为什么不适时的将dbwr写进度反应到文件中?
适时反应成本太高! 3秒种是一个合适的值,可以接受,代价不高又能大大缩短崩溃后恢复时间。
检查点发生以后,CKPT进程检查checkpoint queue(也就是脏块链表)是否过长,如果是,则触发DBWn,将一部分脏块写入数据文件,从而缩短checkpoint queue。
checkpoint 发生时,一方面通知dbwr进行下一批写操作,(dbwr 写入的时候,一次写的块数是有一个批量写的隐藏参数控制的。)另一方面,oracle 采用了一个心跳的概念,以3秒的频率将dbwr 写的进度反应到控制文件中,也就是把dbwr当前刚写完的dirty buffer对应的scn 写入数据文件头和控制文件,这就是检查点scn。
这个3秒和增量检查点不是一个概念,3秒只是在控制文件中,ckpt 进程去更新当前 dbwr写到哪里了,这个对于 ckpt 进程来说叫 heartbeat ,heartbeat是3秒一次,3秒可以看作不停的检查并记录检查点执行情况(DBWR的写进度)。
检查点发生之后数据库的数据文件、控制文件处于一致状态的含义是不需要进行 介质恢复,只表示数据文件头一致,但是并不表示数据文件内容一致,因为数据文件内容可能在没有发生检查点的其他情况下的dbwr写数据文件,这样数据文件内容就不一致,若掉电需要进行崩溃恢复。
二、触发的条件
这里需要明白两个概念“完全检查点和增量检查点”的区别。
增量检查点(incremental checkpoint)
oracle8之前,那时候没有chekpoint queue,也没有增量的概念,dirty buffer 的写出是无顺的,就是冻结所有 dml 等候所有dirty buffer被写出。后来随着数据库规模的扩展和buffer cache 的不断增大,oracle 意识到这个机制已经满足不了需要,所以提出增量检查点的概念,建立了 checkpoint queue ,让 dirty buffer header 根据首次变化时候的顺序排列在queue里面。 这样dbwr只要顺着 queue 的顺序写,而其他进程不必等候dbwr的写操作完成 就可以继续。
自从有了checkpoint queue之后 ,检查点就成为一个短暂的动作,就是通知 dbwr 你要继续写 dirty buffer到 当前检查点发生时候的scn,然后将当前dbwr刚写完的 dirty buffer 对应的scn ,写进数据文件和控制文件【增量检查点时不写数据文件头】(比如日志切换这种动作引起的检查点动作等)。然后检查点动作就结束了。剩下的工作就交给DBWn了,检查点进程也不必等候dbwr的完成。
ckpt 进程通知 dbwr 之后并不需要等待 dbwr 写到当前这个检查点对应的时间点。所以ckpt 可以将已经完成的最后一个检查点scn写到控制文件和数据文件(可能是上一个,也可能是上上个,总之dbwr完成了哪个算哪个)。 这样本次需要写进数据文件的dirty buffer 可能在下一次检查点发生的时候已经写完了,这样下一次检查点发生的时候就把本次的检查点scn 更新到控制文件和数据文件。
在oracle 8之前,没有ckpt queue,只有LRU list,而LRU list里面的Dirty Buffer是不按时间顺序排列的,所以checkpoint时都会做一个full thread checkpoint,将LRU list中的所有buffer 写到数据文件。
在oracle8i以后,当发生FULL CHECKPOINT时,oracle只是获取系统当前的SCN,然后将这个SCN之前的脏数据块写入磁盘,后续的DML脏数据块继续入队,他并不是保证整个缓冲区没有脏块,只是保证 此检查点发生之前这段距离间没有脏块,而在checkpoint点之后的DML可以正常操作。
oracle8以后推出了incremental checkpoint的机制,在以前的版本里每checkpoint时都会做一个full thread checkpoint,这样的话所有脏数据会被写到磁盘,巨大的i/o对系统性能带来很大影响。为了解决这个问题,oracle引入了checkpoint queue机制,每一个脏块会被移到检查点队列里面去,按照low rdb(第一次对此块修改对应的redo block address)来排列,靠近检查点队列尾端的数据块的low rba值是最小的,而且如果这些赃块被再次修改后它在检查点队列里的顺序也不会改变,这样就保证了越早修改的块越早写入磁盘。每隔3秒钟ckpt会去更新控制文件和数据文件,记录checkpoint执行的情况。
在运行的Oracle 数据中,有很多事件、条件或者参数来触发检查点。比如
l 当已通过正常事务处理或者立即选项关闭例程时;(shutdown immediate或者Shutdown normal;)
l 当通过设置初始化参数LOG_CHECKPOINT_INTERVAL、LOG_CHECKPOINT_TIMEOUT 和FAST_START_IO_TARGET 强制时;
l 当数据库管理员手动请求时;(ALter system checkpoint)
l alter tablespace ... offline;
l 每次日志切换时;(alter system switch logfile)
需要说明的是,alter system switch logfile也将触发完全检查点的发生。
alter database datafile ... offline不会触发检查点进程。
如果是单纯的offline datafile,那么将不会触发文件检查点,只有针对offline tablespace的时候才会触发文件检查点,这也是为什么online datafile需要media recovery而online tablespace不需要。
对于表空间的offline后再online这种情况,最好做个强制的checkpoint比较好。
上面几种情况,将触发完全检查点,促使DBWR 将检查点时刻前所有的脏数据写入数据文件。
另外,一般正常运行期间的数据库不会产生完全检查点,下面很多事件将导致增量检查点,比如:
在联机热备份数据文件前,要求该数据文件中被修改的块从DB_Buffer 写入数据文件中。所以,发出这样的命令:
l ALTER TABLESPACE tablespace_name BIGEN BACKUP & end backup; 也将触发和该表空间的数据文件有关的局部检查点;另外,
l ALTER TABLESPACE tablespace_name READ ONLY;
l ALTER TABLESPACE tablespace_name OFFLINE NORMAL;
等命令都会触发增量检查点。
注意:
每隔三秒也会触发检查点,但是 并没有被oracle正式作为一种检查点的触发方式列入文档 ,并且这个3秒是记录 dbwr进度而不是通知dbwr写。
这是三秒触发的检查点与其它条件触发检查点不同的地方。