一、检查点概述
大多数关系型数据库都采用"在提交时并不强迫针对数据块的修改完成"而是"提交时保证修改记录(以重做日志的形式)写入日志文件"的机制,来获得性能的优势。这句话的另外一种描述是:当用户提交事务,写数据文件是"异步"的,写日志文件是"同步"的。这就可能导致数据库实例崩溃时,内存中的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进程的检查点和心跳。
检查点发生以后,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以后推出了incremental checkpoint的机制,在以前的版本里每checkpoint时都会做一个full thread checkpoint,这样的话所有脏数据会被写到磁盘,巨大的i/o对系统性能带来很大影响。为了解决这个问题,oracle引入了checkpoint queue机制,每一个脏块会被移到检查点队列里面去,按照low rdb(第一次对此块修改对应的redo block address)来排列,靠近检查点队列尾端的数据块的low rba值是最小的,而且如果这些赃块被再次修改后它在检查点队列里的顺序也不会改变,这样就保证了越早修改的块越早写入磁盘。每隔3秒钟ckpt会去更新控制文件和数据文件,记录checkpoint执行的情况。
在运行的Oracle 数据中,有很多事件、条件或者参数来触发检查点。比如
当已通过正常事务处理或者立即选项关闭例程时;(shutdown immediate或者Shutdown normal;)
当通过设置初始化参数LOG_CHECKPOINT_INTERVAL、LOG_CHECKPOINT_TIMEOUT 和FAST_START_IO_TARGET 强制时;
当数据库管理员手动请求时;(ALter system checkpoint)
alter tablespace ... offline;
每次日志切换时;(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 写入数据文件中。所以,发出这样的命令:
ALTER TABLESPACE tablespace_name BIGEN BACKUP & end backup; 也将触发和该表空间的数据文件有关的局部检查点;另外,
ALTER TABLESPACE tablespace_name READ ONLY;
ALTER TABLESPACE tablespace_name OFFLINE NORMAL;
等命令都会触发增量检查点。
关于检查点的一点具体应用讨论:
Commit成功后,数据还会丢失吗?
对于Oracle来说,用户所做的DML操作一旦被提交,则先是在database buffer cache 中进行修改,同时在修改之前会将数据的前镜像保存在回滚段中,然后将修改之前和修改之后的数据都写入到redo log buffer中,当接收到commit命令之后,则redo log buffer开始写redo log file ,并且记录此时的scn,当redo log file 写完了之后,表示这次事务提交操作已经确认被数据库记录了,只有当redo log file 写成功了,才会给用户 Commit completed 的成功字样。而对于Database buffer cache中的dirty buffer则会等待触发DBWn才写入,但是如果此时断电,则数据已经被记录到了redo log file中,系统在重新启动的时候,会自动进行嵌滚和回滚来保证数据的一致。所以,只要是commit成功的了,数据不会丢失!
数据库发生一次DBWn,是否将所有buffer cache 中的dirty buffer 都写入,还是先将脏队列中的数据写入?
这话看起来有道理,但实际上,dbwr在写的时候又不断地在产生dirty buffer ,所以说检查点发生的时候是期望把该时间点之前的所有脏缓冲区写入数据文件。
所有的buffer,不在LRU list上就在dirty list上, dbwr写入的时候,一次写的块数是有一个批量写的隐藏参数控制的。
所以说要是 dbwr将 dirty list也好, lru list上的也好,要实现全部写入,都是一个现实系统中很难存在的现象。dirty 总是在不断的产生,dbwr总是在不断地写,增量检查点发生的时候也并不意味着一定要更新数据文件头,检查点开始的时候只表示该次检查点结束的时候要更新数据文件头的话数据文件头具有该时间点的一致性。
关于检查点等待事件:
有些事件的产生必须等待检查点的完成,才能开始数据库的其它操作:
日志文件切换就是其中一个事件,日志切换必须等待检查点完成。
log file switch (checkpoint incomplete) 这个等待事件本身也说明,日志切换时产生的检查点是需要等待的,这个日志文件所对应脏块全部写完后,检查点进程更新控制文件和数据头,然后这个检查点才能算完成。
也就是说日志切换必须等待检查点完成,而检查点在等待DBWn完成。
这种等待DBWn完成的检查点,最后一步写入控制文件和数据文件头的SCN,肯定是DBWn完成的最后一块的SCN。
检查点为什么要等待dbwr完成后才进行切换(log switch)?
log switch时,是不能立即switch到active状态的,log group必须等待。active表示该log group还没有完成归档(归档模式下)或者该log group有进行instance recovery的需要用到的日志。所以当checkpoint发生时,如果dbwr还没有写完它该写完的dirty buffers(该checkpoint时间点以前产生的dirty buffers, 靠scn判断),则该log group处于active状态,不会进行日志切换,当然也不会发生日志文件被覆盖的问题了。
如果没有设置archive log ,在检查点发生后,发生log switch一个轮回,log file是否会被覆盖掉?
当检查点发生后,会触发lgwr,lgwr会把此时SCN以前在redo buffer中的所有操作写到redo log file,同时lgwr也会触发dbwr进程,dbwr也开始把此刻以前database buffer中的dirty buffer队列中的操作写入data file。
其实检查点发生后,就是lgwr和dbwr写buffer到磁盘文件的过程,但是两者的读写速度时不同的,dbwr写buffer到数据文件的过程相对较慢,因为dbwr写过程是一个随机读取存储的过程。Lgwr写buffer到redo文件的过程比dbwr要快很多,因为lgwr是顺序读取写入的。
由于以上lgwr和dbwr写操作的速度不同,就产生了一个等待问题。即当lgwr
轮循一圈后,要进行日志切换,覆盖redo log file,但是此时dbwr还没有写完,那么lgwr就会出现等待,oracle也会hang在那里,同时alter文件中会提示一个相应的提示checkpoint not complete。等检查点完成后数据库自动恢复正常。
当log switch时,会触发检查点,标记检查点时,DBWR要把log file中covered by the log being checkpointed的数据写入到数据文件上,如果Dbwr没有写完,那么前一个logfile是不能被覆盖的。因此必须等检查点完毕,也可以说是将 要被覆盖的日志相关的数据块全部写入数据文件后,该日志文件才能被覆盖!
如果这种情况出现,alert文件中会记录checkpoint not complete事件,这通常表明你的dbwr写入过慢或者logfile组数过少或log file太小。
检查点发生时,出现日志切换,但是dbwr还没有写完,是否会覆盖redo log file,如果此时掉电,dbwr挂起,会出现丢失数据吗?
答:不会覆盖redo文件,要等待dbwr写完才覆盖。或者说要等待检查点完成才覆盖。
如果DBWn正在写的脏块是指对应到即将被覆盖的那个日志文件的,那么,因为在这些操作完成之前,不可能有覆盖这件事发生,假设你预料的掉电发生了,不会有什么问题,实例恢复时的前滚从刚才准备覆盖的那个日志文件开始。
如果正在写的脏块是指对应到刚刚被写满的那个日志文件的,肯定一时半会也写不完,日志切换也不会等它,如果切换成功后dbwr挂了,后面的写脏块操作估计也还没完成,但是,刚写满的那个文件并没有被覆盖,也不会有任何问题。
因此不会出现数据丢失!
当一个很大的dml发生时,用户在commit后,需要将redo log buffer中的脏数据写入redo log file中。如果在写的过程中,发现一个redo log file写不下的话,需要写另外一个redo log file,这时应该触发checkpoint,接着触发DBWn,将脏数据写入datafile,这时发生掉电,导致DBWR失败。这时其实就可以说commit失败了。以上所述其实就是commit没有提交成功。
alter systen switch logfile会触发完全检查点;但是为什么,日志切换以后检查点只能记录到上一次归档日志产生的时间呢?而不是现在归档日志产生的时间呢?
因为这时的checkpoint enqueue队列中,也就是脏块队列中脏块还不够多,还不足以触发DBWn写脏块。所以,内存里需要写入的脏块所对应的redo还存在于上一个redo log里,这时你去看该redo log的status为active。
下面操作日志中出现当前redo log的status为current,而上一个redo log的status为active就说明了这一切。上一个redo log的status为active正是说明在上一个redo log中还存在未写入datafile的block所对应的redo。
SQL> create table gaojf3 as select * from all_objects;
Table created
SQL> insert into gaojf3 select * from gaojf3;
29630 rows inserted
SQL> /
59260 rows inserted
SQL> commit;
Commit complete
SQL> select * from x$kccrt;
ADDR INDX INST_ID RTNUM RTSTA RTCKP_SCN RTCKP_TIM RTCKP_THR RTCKP_RBA_SEQ RTCKP_RBA_BNO RTCKP_RBA_BOF RTCKP_ETB RTOTF RTOTB RTNLF RTLFH RTLFT RTCLN RTSEQ RTENB RTETS RTDIS RTDIT RTLHP RTSID RTOTS
-------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------- ------------- ------------- ------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------------- -------------------- ---------- ---------------- --------------------
421A54D0 0 1 1 15 8796119126928 08/08/2006 00:28:38 1 14 2 16 0200000000000000 0 0 3 1 3 1 14 8796117861680 08/02/2006 02:29:36 0 13 cicro 08/02/2006 02:33:50
SQL> alter system switch logfile;
System altered
SQL> select * from x$kccrt;
ADDR INDX INST_ID RTNUM RTSTA RTCKP_SCN RTCKP_TIM RTCKP_THR RTCKP_RBA_SEQ RTCKP_RBA_BNO RTCKP_RBA_BOF RTCKP_ETB RTOTF RTOTB RTNLF RTLFH RTLFT RTCLN RTSEQ RTENB RTETS RTDIS RTDIT RTLHP RTSID RTOTS
-------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------- ------------- ------------- ------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------------- -------------------- ---------- ---------------- --------------------
421A54D0 0 1 1 15 8796119126928 08/08/2006 00:28:38 1 14 2 16 0200000000000000 0 0 3 1 3 2 15 8796117861680 08/02/2006 02:29:36 0 14 cicro 08/02/2006 02:33:50
SQL> select * from x$kccrt;
ADDR INDX INST_ID RTNUM RTSTA RTCKP_SCN RTCKP_TIM RTCKP_THR RTCKP_RBA_SEQ RTCKP_RBA_BNO RTCKP_RBA_BOF RTCKP_ETB RTOTF RTOTB RTNLF RTLFH RTLFT RTCLN RTSEQ RTENB RTETS RTDIS RTDIT RTLHP RTSID RTOTS
-------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------- ------------- ------------- ------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------------- -------------------- ---------- ---------------- --------------------
421A54D0 0 1 1 15 8796119126928 08/08/2006 00:28:38 1 14 2 16 0200000000000000 0 0 3 1 3 2 15 8796117861680 08/02/2006 02:29:36 0 14 cicro 08/02/2006 02:33:50
SQL> select * from x$kccrt;
ADDR INDX INST_ID RTNUM RTSTA RTCKP_SCN RTCKP_TIM RTCKP_THR RTCKP_RBA_SEQ RTCKP_RBA_BNO RTCKP_RBA_BOF RTCKP_ETB RTOTF RTOTB RTNLF RTLFH RTLFT RTCLN RTSEQ RTENB RTETS RTDIS RTDIT RTLHP RTSID RTOTS
-------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------- ------------- ------------- ------------- ---------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------------- -------------------- ---------------- -------------------- ---------- ---------------- --------------------
421A54D0 0 1 1 15 8796119128114 08/08/2006 00:35:51 1 15 2 16 0200000000000000 0 0 3 1 3 3 16 8796117861680 08/02/2006 02:29:36 0 15 cicro 08/02/2006 02:33:50
从上面试验中可以看到,完全检查点正在执行,还没有执行完毕。因此,切换日志前后察看x$kccrt,看到的RTCKP_SCN, RTCKP_TIM没有发生变化。等检查点执行完毕后(也就是检查点触发的dbwr执行完写操作后),rtckp_scn, rtckp_tim才发生变化。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/509190/viewspace-861444/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/509190/viewspace-861444/