深入理解检查点(checkpoint)的工作机制

本文结合Eygle大师的力作《深入解析oracle》一书中有关Checkpoint一节内容以及网上其它同学的相关知识,加上自己的理解,并配上实例深入的去理解Checkpoint的机制,如有不对的地方还请高手指点,希望与各位共同学习。欢迎转载,转载请注明出处。

1、坚持点的基础知识

检查点是数据库的一个事件,它存在的根本意义在于减少崩溃恢复(Crash recover)时间。检查点事件有CKPT进程触发,当检查点发生时,CKPT进程负责通知DBWR进程将脏数据(Dirty buffer)写出到数据文件上。CKPT还有一个职责就是负责更新数据文件头和控制文件上的检查点信息。

2、坚持点的工作原理

  在oracle数据库中,当进行数据修改时,需要首先将数据读入内存中(Buffer cache),修改数据的同时,oracle会记录重做(redo)信息用于恢复。因为有了重做的存在,oracle不需要在事务提交时(commit)立即将变化的数据写回磁盘(立即写可能太频繁导致性能大大降低),重做的存在正是为了在数据库崩溃之后,数据可以恢复。
  最常见的情况,数据库可能因为断电而crash,那么内存中修改过的、尚未写入数据文件的数据将会丢失。在下一次数据库启动之后,oracle需要通过redo日志进行数据库事务重演(也就是进行前滚),将数据库恢复到崩溃之前的状态,然后数据库可以打开提供服务,之后oracle将未提交的事务进行回滚。
  在启动的过程中需要多久才能打开数据库呢?我们希望这个时间越短越好,检查点的存在也正是为了缩短这个恢复时间。当检查点发生时(此时SCN被称为Checkpoint SCN)oracle通知DBWR进程,把修改过的数据,也就是此时Checkpoint SCN之前的脏数据(Dirty data)从buffer cache写入磁盘,当写完之后CKPT进程则会相应地更新数据文件头和控制文件,记录坚持点信息,变更标识。在坚持点完成之后,此检查点之前的被修改过的数据都已经写回磁盘,重做日志中的相应记录对于崩溃后实例恢复已经不再有用。

如下图所示,假定在T1时间点,数据库完成并记录了最后一次checkpoint,在T2时刻数据库crash,那么在下次启动数据库的时候,T1时刻之前的redo不再需要进行恢复,oracle只需要应用T1到T2之间的redo日志就可以把数据库恢复到实例crash之前的状态。

bb

3、常规坚持点和增量检查点


为了理解常规坚持点和增量坚持点的概念,首先需要了解脏缓冲队列(Dirty list)。当数据在Buffer cache被修改之后,Dirty buffer就会被移动到Dirty list上,以便将来执行的检查点可以将这些修改过的Buffer写到数据文件上。但是Dirty buffer上的buffer并没有有顺序,有的buffer反复被修改,在链表上的位置就可能发生变化,当检查点发生时oracle需要将脏缓冲队列上的全部数据写出到数据文件。为了区分,在oracle8之前,oracle实施的这类坚持点通常被称为常规检查点(Conventional Checkpoint),由于常规检查点时需要写出全部脏数据,所以也被称为完全坚持点(Complete Checkpoint)。常规检查点按特定的条件触发(log_chechpoint_interval、log_checkpoint_timeout参数设置及log switch等条件触发),触发时会同时更新数据文件头和控制文件以记录检查点信息。
从oracle8开始,oracle演进了新的算法,进而引入了增量检查点(Incremental Checkpoint)的概念。和以前的版本相比,在新版本中,主要的变化是引入了检查点队列(Checkpoint Queue,CKPTQ)机制。在数据库内部,每一个脏数据库块都会被记录到检查点队列,按照LRBA(Low RBA,第一次对此数据块修改对于的Redo Byte Address)的顺序来排列,如果一个数据块进行多次修改,该数据块在检查点队列的顺序并不会发生变化(相对于LRBA,后面修改的RBA称为HRBA)。
在执行增量检查点时,CKPT告诉 DBWRdirty buffer要一直写到Checkpoint Queue中的一个最新位置,在这个检查点过程中,可以继续产生dirty bufferDBWR也不是一次要把所有dirty buffer 全部写到磁盘(不同于完全检查点的地方),这样就提高了检查点的效率,使得数据库要做恢复的时候从这个最新位置开始做恢复,而不是从数据文件中的checkpoint scn (上一个完全检查点位置)开始做恢复,这样将缩短恢复时间。DBWR写到了哪个最新的位置是通过CKPT heartbeat来实现的,具体的说就是每3秒种CKPT会在控制文件中更新CKPT写到那个最新的位置了,这样实例恢复的时候就从控制文件中记录的最新位置开始恢复。同时,CKPT进程也阶段性地使用非常轻量级的控制文件更新协议,将当前的最低RBA写入控制文件,为了减少频繁增量检查点的性能影响,CKPT在进行轻量级更新时并不会改写控制文件中数据文件的检查点信息以及数据文件的头信息,而只是记录控制文件检查点SCN(Controlfile Checkpoint at scn),并且根据增量检查点的写出增进RBA信息。
我的数据库有12组redo log,为了模拟数据库繁忙的情况,我快速收到switch logfile 12次。从下面alert日志中记录的过程我们可以看到,刚开始Checkpoint Queue是在RBA [0x1760f.2.10],过了一段时间之后数据库进行了一次增量Checkpoint,所以在RBA [0x1760f.2.10]之前的Dirty Buffer都会被写入到数据文件。此后由于我快速的switch log所以redi log快速的切换,并且把RBA加入到Checkpoint Queue中,此时已经切换过的redo log的状态时active,因为在这个过程中并没有到达触发增量Checkpoint的条件,当切换一轮之后oracle发现除了current的redo之外其它的redo状态都是active,现在已经没有可用的redo做切换,oracle不得已刷新Checkpoint Queue将发现的最大的LRBA RBA [0x1761a.2.10]之前的所有已经加入到Checkpoint Queue的Dirty Buffer写入到数据文件。所以alert日志中紧接着可以看到所有Checkpoint Queue上的Dirty buffer都被写入到数据文件(即出现了很多类似Completed checkpoint up to RBA [0x17611.2.10]),直到完成最大的LRBA RBA [0x1761a.2.10]本次增量Checkpoint才算完成。完成的过程还可以看到其实在去写Dirty Buffer的时候并不一定按加入Checkpoint队列的顺序(LRBA)去写这可能跟参数db_writer_processes有关,当多个进程同时写的时候并不能保证按LRBA顺序完成【关于这个过程eygle大师的书上这样写的:DBWR从检查点队列按照Low RBA的顺序写出,此时先修改的数据就可以被按顺序优先写出,实例检查点因此可以不断增进】。

Beginning log switch checkpoint up to RBA [0x1760f.2.10], SCN: 8486535249241

Thread 1 advanced to log sequence 95759

  Current log# 8 seq# 95759 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo08.log

Thu Mar 17 10:29:24 2011

Completed checkpoint up to RBA [0x1760f.2.10], SCN: 8486535249241

Thu Mar 17 10:32:37 2011

Beginning log switch checkpoint up to RBA [0x17610.2.10], SCN: 8486536488869

Thread 1 advanced to log sequence 95760

  Current log# 9 seq# 95760 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo09.log

Thu Mar 17 10:33:37 2011

Beginning log switch checkpoint up to RBA [0x17611.2.10], SCN: 8486536865326

Thread 1 advanced to log sequence 95761

  Current log# 10 seq# 95761 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo10.log

Thu Mar 17 10:33:57 2011

Beginning log switch checkpoint up to RBA [0x17612.2.10], SCN: 8486536868604

Thread 1 advanced to log sequence 95762

  Current log# 11 seq# 95762 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo11.log

Beginning log switch checkpoint up to RBA [0x17613.2.10], SCN: 8486536870570

Thread 1 advanced to log sequence 95763

  Current log# 12 seq# 95763 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo12.log

Thu Mar 17 10:34:43 2011

Beginning log switch checkpoint up to RBA [0x17614.2.10], SCN: 8486537091802

Thread 1 advanced to log sequence 95764

  Current log# 4 seq# 95764 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo04.log

Beginning log switch checkpoint up to RBA [0x17615.2.10], SCN: 8486537092511

Thread 1 advanced to log sequence 95765

  Current log# 1 seq# 95765 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo01.log

Thu Mar 17 10:34:58 2011

Beginning log switch checkpoint up to RBA [0x17616.2.10], SCN: 8486537098178

Thread 1 advanced to log sequence 95766

  Current log# 2 seq# 95766 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo02.log

Beginning log switch checkpoint up to RBA [0x17617.2.10], SCN: 8486537098724

Thread 1 advanced to log sequence 95767

  Current log# 3 seq# 95767 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo03.log

Beginning log switch checkpoint up to RBA [0x17618.2.10], SCN: 8486537099018

Thread 1 advanced to log sequence 95768

  Current log# 5 seq# 95768 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo05.log

Thu Mar 17 10:35:21 2011

Beginning log switch checkpoint up to RBA [0x17619.2.10], SCN: 8486537111235

Thread 1 advanced to log sequence 95769

  Current log# 6 seq# 95769 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo06.log

Beginning log switch checkpoint up to RBA [0x1761a.2.10], SCN: 8486537111653

Thread 1 advanced to log sequence 95770

  Current log# 7 seq# 95770 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo07.log

Thread 1 cannot allocate new log, sequence 95771

Checkpoint not complete

  Current log# 7 seq# 95770 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo07.log

Thu Mar 17 10:35:26 2011

Beginning log switch checkpoint up to RBA [0x1761a.2.10], SCN: 8486537111653

Thread 1 advanced to log sequence 95770

  Current log# 7 seq# 95770 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo07.log

Thread 1 cannot allocate new log, sequence 95771

Checkpoint not complete

  Current log# 7 seq# 95770 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo07.log

Thu Mar 17 10:35:26 2011

Completed checkpoint up to RBA [0x17610.2.10], SCN: 8486536488869

Thu Mar 17 10:35:26 2011

Beginning log switch checkpoint up to RBA [0x1761b.2.10], SCN: 8486537113134

Thread 1 advanced to log sequence 95771

  Current log# 8 seq# 95771 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo08.log

Thu Mar 17 10:37:55 2011

WARNING: inbound connection timed out (ORA-3136)

Thu Mar 17 10:38:42 2011

Completed checkpoint up to RBA [0x17611.2.10], SCN: 8486536865326

Thu Mar 17 10:39:03 2011

Completed checkpoint up to RBA [0x17612.2.10], SCN: 8486536868604

Completed checkpoint up to RBA [0x17613.2.10], SCN: 8486536870570

Thu Mar 17 10:39:15 2011

Incremental checkpoint up to RBA [0x17613.2068.0], current log tail at RBA [0x1761b.6ae1c.0]

Thu Mar 17 10:39:48 2011

Completed checkpoint up to RBA [0x17614.2.10], SCN: 8486537091802

Completed checkpoint up to RBA [0x17615.2.10], SCN: 8486537092511

Thu Mar 17 10:40:03 2011

Completed checkpoint up to RBA [0x17617.2.10], SCN: 8486537098724

Completed checkpoint up to RBA [0x17616.2.10], SCN: 8486537098178

Completed checkpoint up to RBA [0x17618.2.10], SCN: 8486537099018

Thu Mar 17 10:40:27 2011

Completed checkpoint up to RBA [0x1761a.2.10], SCN: 8486537111653

Completed checkpoint up to RBA [0x17619.2.10], SCN: 8486537111235

Completed checkpoint up to RBA [0x1761b.2.10], SCN: 8486537113134

Thu Mar 17 10:46:42 2011


前面是模拟数据库繁忙的情况,在数据库空闲的状态下又是什么样的呢,其实就是上面例子最前面的部分。如下所示,数据库自动switch logfile,经过一段时间之后达到了触发增量Checkpoint或者刷新Dirty Buffer的条件,数据库就会启动DBWR进程将Dirty Buffer中的数据写入到数据文件。

Beginning log switch checkpoint up to RBA [0x1761c.2.10], SCN: 8486539011972

Thread 1 advanced to log sequence 95772

  Current log# 9 seq# 95772 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo09.log

Thu Mar 17 10:51:48 2011

Completed checkpoint up to RBA [0x1761c.2.10], SCN: 8486539011972


从oracle8i开始完全检查点只在以下两种情况出现:1、ALTER SYSTEM CHECKPOINT;2、SHUTDOWN IMMEDIATE OR NORMAL;所以log switch也是触发增量检查点,但是log switch的过程会触发的检查点会促使数据文件和控制文件信息的同步,下面过程是在每次log switch之后加上ALTER SYSTEM CHECKPOINT;完全检查点发生时会完成该完全检查点之前的所有增量检查点。

Beginning log switch checkpoint up to RBA [0x17620.2.10], SCN: 8486544501469

Thread 1 advanced to log sequence 95776

  Current log# 4 seq# 95776 mem# 0: /paic/bj/lbs/redo/oradata/lubj0/redo04.log

Thu Mar 17 11:13:21 2011

Beginning global checkpoint up to RBA [0x17620.7.10], SCN: 8486544501473

Completed checkpoint up to RBA [0x17620.7.10], SCN: 8486544501473

Completed checkpoint up to RBA [0x17620.2.10], SCN: 8486544501469

Thu Mar 17 11:13:22 2011


除了检查点队列(CKPTQ)之外,数据库中还存在另外一个队列和检查点有关,这就是文件检查点队列(FILE QUEUE),通常称为FILEQ,文件检查点的引入提供了表空间相关的检查点的性能。每个Dirty Buffer同时链接到这两个队列,CKPTQ包含实例所有需要执行检查点的Buffer,FILEQ包含属于特定文件需要执行的检查点Buffer,每个文件都包含一个文件队列,在执行表空间检查点请求时需要使用FILEQ,通常当对表空间执行Offline等操作时会触发表空间检查点。CKPTQ和FILEQ都是双向链表,每个队列中都记录了两个地址信息,分别是前一块和下一块Buffer的地址信息。注意只有Dirty Buffer才会包含CKPTQ信息,否则为NULL,信息类似"ckptq:[NULL]fileq:[NULL]"。

4、检查点(checkpoint)的工作机制

检查点是一个数据库事件,它把修改数据从高速缓存写入磁盘,并更新控制文件和数据文件,总结起来如下:

检查点分为三类:
1)局部检查点:单个实例执行数据库所有数据文件的一个检查点操作,属于此实例的全部脏缓存区写入数据文件。
触发命令:
svmrgrl>alter system checkpoint local;
这条命令显示的触发一个局部检查点。
2)全局检查点:所有实例(对应并行数据服务器)执行数据库所有所有数据文件的一个检查点操作,属于此实例的全部脏缓存区写入数据文件。
触发命令
svrmgrl>alter system checkpoint global;
这条命令显示的触发一个全局检查点。
3)文件检查点:所有实例需要执行数据文件集的一个检查点操作,如使用热备份命令alter tablespace USERS begin backup,或表空间脱机命令alter tablespace USERS offline,将执行属于USERS表空间的所有数据文件的一个检查点操作。

检查点处理步骤:
1)获取实例状态队列:实例状态队列是在实例状态转变时获得,ORACLE获得此队列以保证检查点执行期间,数据库处于打开状态;
2)获取当前检查点信息:获取检查点记录信息的结构,此结构包括当前检查点时间、活动线程、进行检查点处理的当前线程、日志文件中恢复截止点的地址信息;
3)缓存区标识:当数据在buffer cache中做了修改之后会自动被为脏缓冲区,加入到Checkpoint Queue的脏缓冲区队列。
4)脏缓存区刷新:当检查点发生时,会到CKPTQ中的脏缓冲区队列找到到目前为止最大的LRBA,并通知DBWR进程将所有脏缓存区写入磁盘,完成之后设置一标志,标识已完成脏缓存区至磁盘的写入操作,以便刷新脏缓冲队列(此时DML可以继续进行)。系统进程LGWR与CKPT进程将继续进行检查,直至DBWR进程结束为止;
5)更新控制文件与数据文件。
注:控制文件与数据文件头包含检查点结构信息。
在两种情况下,文件头中的检查点信息(获取当前检查点信息时)将不做更新:
1)数据文件不处于热备份方式,此时ORACLE将不知道操作系统将何时读文件头,而备份拷贝在拷贝开始时必须具有检查点SCN;
ORACLE在数据文件头中保留一个检查点的记数器,在正常操作中保证使用数据文件的当前版本,在恢复时防止恢复数据文件的错误版本;即使在热备份方式下,计数器依然是递增的;每个数据文件的检查点计数器,也保留在控制文件相对应数据文件项中。
2)检查SCN小于文件头中的检查点SCN的时候,这表明由检查点产生的改动已经写到磁盘上,在执行全局检查点的处理过程中,如果一个热备份快速检查点在更新文件头时,则可能发生此种情况。应该注意的是,ORACLE是在实际进行检查点处理的大量工作之前捕获检查SCN的,并且很有可能被一条象热备份命令 alter tablespace USERS begin backup进行快速检查点处理时的命令打断。
ORACLE在进行数据文件更新之前,将验证其数据一致性,当验证完成,即更新数据文件头以反映当前检查点的情况;未经验证的数据文件与写入时出现错误的数据文件都被忽略;如果日志文件被覆盖,则这个文件可能需要进行介质恢复,在这种情况下,ORACLE系统进程DBWR将此数据文件脱机。

检查点算法描述:
脏缓存区用一个新队列链接,称为检查点队列。对缓存区的每一个改动,都有一个与其相关的重做值。检查点队列包含脏的日志缓存区,这些缓存区按照它们在日志文件中的位置排序,即在检查点队列中,缓存区按照它们的LRBA进行排序。需要注意的是,由于缓存区是依照第一次变脏的次序链接到队列中的,所以,如果在缓存区写出之前对它有另外的改动,链接不能进行相应变更,缓存区一旦被链接到检查点队列,它就停留在此位置,直到将它被写出为止。

ORACLE系统进程DBWR在响应检查点请求时,按照这个队列的LRBA的升序写出缓存区。每个检查点请求指定一个重做值,一旦DBWR写出的缓存区重做值等于或大雨检查点的重做值,检查点处理即完成,并将记录到控制文件与数据文件。
由于检查点队列上的缓存区按照低重做值进行排序,而DBWR也按照低重做值顺序写出检查点缓存区,故可能有多个检查点请求处于活动状态,当DBWR写出缓存区时,检查位于检查点队列前端的缓存区重做值与检查点重做值的一致性,如果重做值小于检查点队列前缓存区的低重做值的所有检查点请求,即可表示处理完成。当存在未完成的活动检查点请求时,DBWR继续写出检查点缓存区。

算法特点:
1)DBWR能确切的知道为满足检查点请求需要写那些缓存区;
2)在每次进行检查点写时保证指向完成最早的(具有最低重做值的)检查点;
3)根据检查点重做值可以区别多个检查点请求,然后按照它们的顺序完成处理。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/15779287/viewspace-671350/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/15779287/viewspace-671350/

你可能感兴趣的:(深入理解检查点(checkpoint)的工作机制)