简答题 1.  什么是检查点?如何调优检查点?
【正确答案】(1)什么是检查点?一般所说的检查点是一种将内存中的已修改数据块与磁盘上的数据文件进行同步的数据库事件(Event),是Oracle在数据库一致性关闭、实例恢复和Oracle基本操作不可缺少的机制。Oracle通过检查点确保被事务修改过的数据可以被同步至磁盘。检查点信息(Checkpoint Information)包含检查点位置(Checkpoint Position)、SCN、联机Redo日志中开始恢复的位置等。当检查点发生时,CKPT通知DBWn进程将脏块(Dirty Buffer)写出到数据文件上,并更新数据文件头及控制文件上的检查点信息。注意,CKPT进程不负责Buffer Cache中的脏数据写入到磁盘中,该工作由DBWn负责:CKPT进程也不负责将Redo Log Buffer中的数据写入到联机Redo日志文件中,该工作由LGWR负责。由于Oracle事务在提交的时候不会将已修改数据块同步写入磁盘上,所以,CKPT进程负责更新控制文件和数据文件头的检查点信息和触发DBWn写脏数据到磁盘。CKPT执行越频繁,DBWn写出就越频繁。
   检查点位置(Checkpoint Position)由在数据缓冲池中存在的最老的脏数据位置决定,并且检查点的信息存储在控制文件和数据文件头中。控制文件中记录的检查点位置是实例恢复的起点。在检查点位置前的Redo记录,其对应的Buffer Cache中的Dirty Buffer已经被写进了数据文件,在此位置后的Redo记录,所对应数据脏块有可能还在内存中。如果发生了实例崩溃,只需要在日志文件中找到检查点位置(Low Cache RBA),从此处开始应用所有的Redo日志文件,就完成了前滚操作。
   (2)检查点的作用  检查点的主要目的是以对数据库的日常操作影响最小的方式刷新脏块,检查点主要有两个作用:
   1)确保数据库的一致性,包含两个方面,第一,确保Buffer Cache中的Dirty Buffer能有规律地定期写入磁盘,这样在系统或数据库出现故障时就不会丢失数据;第二,确保数据库在一致性关闭期间可以将所有已提交了的数据写入磁盘。
   2)实现更快的数据库恢复,主要是缩短实例恢复的时间。实例恢复要把实例异常关闭前没有写出到硬盘的脏数据通过日志进行恢复,只需要重新应用控制文件中记录的检查点位置之后的联机Redo日志条目即可进行恢复。如果脏块过多,那么实例恢复的时间也会很长,所以,检查点的发生可以减少脏块的数量,从而提高实例恢复的时间。
   (3)检查点的分类CKPT进程负责将检查点信息写入到数据文件头中和控制文件中,包括几种类型的检查点,见表。
   

   (4)有关检查点的几个概念
   1)1RBA(Redo Block Address)、LRBA(Low RBA)、HRBA(High RBA)。RBA就是Redo日志块的地址,相当于数据文件中的ROWID,通过RBA可以定位Redo日志块。RBA由3部分组成:
   ①日志文件序列号(4字节),根据这个可以找到对应的日志文件地址。
   ②日志文件块编号(4字节),根据这个可以找到对应日志条目所在的日志文件块。
   ③Redo日志记录在日志块中的起始偏移字节数(2字节),根据这个可以找到对应的日志条目。
   在Buffer Cache中,一个脏块第一次被更新的时候会产生Redo日志记录,该记录在Redo日志文件中所对应的位置就称为LRBA;若数据库再次更新该脏块的时候也会产生Redo日志记录,则该记录在Redo日志文件中所对应的位置就称为HRBA。
   例如,用户发出了一条UPDATE命令,更新了块A,块A现在变成了脏块,Oracle会为它生成一条Redo记录。这条Redo记录在Redo日志文件中的位置就是RBA。过了一会儿,假如块A依然还是脏块,此时,用户又发出一条更新块A的命令,这又会生成一条Redo记录。第一条更新命令对应的Redo记录的RBA被称为块A的LRBA(Low RBA),第二条更新命令对应的RBA被称为HRBA(High RBA)。
   2)Checkpoint RBA。当一个检查点事件发生的时候,CKPT进程会记录下当时所写的Redo日志块的地址即RBA,此时记录的RBA被称为Checkpoint RBA。从上一个Checkpoint RBA到当前的Checkpoint RBA之间的日志所保护的Buffer Cache中的脏块接下来将会被写入到数据文件当中去。
   3)检查点队列(Checkpoint Queue,CKPTQ)。Oracle将所有在Buffer Cache中被修改的脏块按照LRBA的顺序连接起来就组成了一个检查点队列(CKPTQ),这个队列主要记录了Buffer Cache第一次发生变化的时间顺序,然后由DBWn进程根据CKPTQ顺序将脏块写入到数据文件中,这样保证了先发生变更的Buffer能先被写入到数据文件中。
   Oracle引入检查点队列(CKPTQ)是为了使检查点在Buffer Cache较大的情况下依然有效工作(既要保证数据完全恢复,又要尽量快速恢复),该队列上存放的都是脏块所对应的Buffer Header。每次DBWn写脏块时,也是从CKPTQ上扫描脏块,并将这些脏块实际写入数据文件。检查点队列上的Buffer Header是按照数据块第一次被修改的时间的先后顺序来排列的。越早修改的数据块的Buffer HeaderS)在越前面,同时如果一个数据块被修改了多次的话,那么在该链表上也只出现一次,其在CKPTQ上的位置不会发生变化。而且,检查点队列上的Buffer Header还记录了脏块在第一次被修改时,所对应的Redo条目在Redo日志文件中的地址,也就是LRBA,所以可以认为检查点队列上的脏块是按照LRBA排列的,从而保证最早更改的数据块能够尽快从内存写入数据文件。
   DBWn每到一定的时机,就会被触发,沿着检查点队列(CKPTQ)的顺序刷新脏块,同时CKPT进程监控着检查点队列的长度,当检查点队列的长度达到一定限制时,CKPT会通知DBWn写脏块。CKPT会根据几个参数的设置和I/O的速度以及繁忙程度,计算出来一个Target RBA(目标RBA),DBWn会沿着检查点队列,按照Dirty Buffer的LRBA顺序将所有Target RBA之前对应的脏块从内存写入数据磁盘文件。当CKPT进程通知完DBWn后,CKPT的任务就结束了,它并不会等待DBWn写完所有的Target RBA之前的脏块。因此这里CKPT只是起到了一个通知DBWn进程写入的作用。
   4)文件检查点队列(File Queue,FILEQ)。文件检查点队列提高了表空间检查点(Tablespace Checkpoint)的性能,每个Dirty Buffer同时链接到CKPTQ和FILEQ,CKPTQ包含实例所有需要执行检查点的Buffer,FILEQ包含属于特定文件需要执行检查点的Buffer。每个文件都包含一个文件队列,在执行表空间检查点请求时需要使用FILEQ,通常当对表空间执行OFFLINE等操作时会触发表空间检查点。
   5)On Disk RBA。“On Disk RBA”指向Redo日志文件里最新的(最后的)一条Redo日志条目,它是CKPT进程从某一个脏块里读取过来的,在进行恢复时应用Redo至少要达到这个值。在实例崩溃后,再次启动数据库,Oracle会到控制文件中读取LRBA,这就是检查点位置。从此处开始应用Redo日志,应用到On Disk RBA的位置,On Disk RBA是Oracle前滚操作的终点。如果某条Redo记录的RBA高于On Disk RBA,那么说明此Redo记录还没有被LGWR写进日志文件中,还驻留在Log Buffer中,所以,崩溃发生时,它是不可能被恢复的。
   (5)增量检查点  为了能够尽量减少实例崩溃后恢复的时间,Oracle还引入了增量检查点(Incremental Checkpoint),从而增加了检查点启动的次数。在执行增量检查点时,DBWn从检查点队列按照LRBA顺序写出,先修改的数据可以被按优先顺序写出,全局检查点因此可以不被增进。同时CKPT进程阶段性使用轻量级控制文件更新协议将当前LRBA写入控制文件,CKPT在进行轻量级更新时,不会更新控制文件中数据文件检查点信息(数据库SCN以及数据文件条目的SCN信息)以及数据文件头信息,而只是每3s由CKPT进程检查DBWn写进度并更新控制文件中的检查点的位置信息(LRBA)。完全检查点会将检查点信息写入到控制文件以及数据文件头中;增量检查点只会将LRBA信息写入到控制文件中。
   通过增量检查点,数据库可以将全部写出改为增量渐进写出,从而极大减少对于数据库性能的影响,而检查点队列进一步将RBA和检查点关联起来,从而可以通过检查点确定实例恢复的起点。
   增量检查点的几个作用:
   1)CKPT每3s一次的检查DBWn写进度并在控制文件中记录检查点位置(LRBA)。注意,增量检查点并不会去更新数据文件头以及控制文件中数据库SCN及数据文件条目的SCN信息。
   2)CKPT定期触发DBWn去写CKPTQ中的脏数据。
   (6)检查点调优检查点的主要任务就是催促DBWn刷新脏块,如果DBWn刷新脏块时的等待事件太多,那么就说明脏块太多、存储设备的写速度太慢,或者就是增量检查点的频率设置不合理。DBWn写脏块的等待事件是db file parallel write。如果系统增量检查点频率很低,系统大量产生该事件,在排除了存储设备写性能的问题后,那么应该将增量检查点频率设置得高一些。反之,如果增量检查点频率本身很高,若出现了db file parallel write事件,则说明检查点频率太高了。
   1)与增量检查点相关的参数。优化检查点涉及到下面4个关键初始化参数:
   ①FAST_START_MTTR_TARGET。
   ②LOG_CHECKPOINT_INTERVAL。
   ③LOG_CHECKPOINT_TIMEOUT。
   ④LOG_CHECKPOINTS_TO_ALERT。
   需要注意的是,日志文件切换将始终覆盖由以上4个参数引起的检查点。这4个参数的详解见表。
   

   需要注意的是,除了上表中列出的4个初始化参数外,Oracle内部事实上还将Redo日志文件末尾前面90%的位置设为检查点位置(90% of Smallest Redo Log)。在每个Redo日志中,这几个参数指定的位置可能不尽相同,Oracle将离日志文件末尾最近的那个位置确认为检查点位置。
   在Oracle 9i后,对检查点频率建议只设置FAST_START_MTTR_TARGET。当然,根据需要也可以通过参数LOG_CHECKPOINTT_IMEOUT设置一个脏块保持脏状态的最大时间,而参数LOG_CHECKPOINT_INTERVAL建议不再使用,参数FAST_START_IO_TARGET已废弃。
   2)Redo日志和检查点。在每次切换日志时都会发生一次Thread检查点。如果上一个检查点已在进行中,那么由日志切换引起的检查点将覆盖当前检查点。此时就需要大小合适的Redo日志,以避免因频繁的日志切换而引起不必要的检查点。另外,增量检查点目标和日志尾之间的间隔也会受“最小在线日志文件大小的90%”设置所限制。这样可确保在大多数情况下,日志切换不必等待检查点。因此,日志文件大小应配置得足够大。一个经验值是,最多每15~20min切换一次日志。日志文件过小会增加检查点活动并降低数据库的性能。Oracle建议用户将所有在线日志文件设置为同一大小,且每个线程至少拥有两个日志组。若要监视日志切换发生的速度以及随后的检查点发生的速度,则告警(ALERT)日志是一个很有价值的工具。
   以下是通过ALERT日志发现日志切换过于频繁的示例:
   
   
   如果Redo日志每3min切换一次,那么就会明显感觉到数据库性能降低了。这表明Redo日志不够大,不能有效地处理该事务负载。
   有时候在告警日志(alert_$ORACLE_SID.log)文件中可以看到以下消息:
   
   那么,在告警日志中的“Cannot allocate new log”和“Checkpoint not complete”是什么含义呢?“Cannot allocate new log”表示无法分配新日志,“Checkpoint not Complete”表示检查点未完成。这两个信息表明Oracle希望重新使用某个Redo日志文件,但当前的检查点位置仍位于该日志中。在这种情况下,Oracle必须等到检查点位置通过该日志。由于增量检查点目标相对于当前日志尾的滞后绝不会超过最小日志文件大小的90%以上,因此,如果DBWn写入速度过慢,或者在日志全满之前发生日志切换,或者日志文件过小,就会遇到这种情况。在数据库等待检查点时,Redo生成过程会停止,直到完成日志切换。
   (7)RAC中的检查点集群环境中的检查点可以分为:
   1)局部检查点:单个实例执行数据库所有数据文件的一个检查点操作,属于此实例的全部脏缓存区写入数据文件,触发命令为:
   
   这两条命令都可以显式地触发一个局部检查点。注意,命令“ALTER SYSTEM SWITCH LOGFILE;”只会影响当前的数据库实例。
   2)全局检查点:RAC数据库的所有实例执行数据库所有数据文件的一个检查点操作,属于此实例的全部脏缓存区写入数据文件,触发命令为:
   
【答案解析】