SCN、Redo和Checkpoint

本文深入解析Oracle数据库的恢复机制,包括Redolog的作用、SCN(系统变更号)的应用、检查点(Checkpoint)的过程及其优化参数等内容。通过这些核心概念的学习,帮助读者理解Oracle如何在异常关机后恢复数据一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Redo log作用

数据库异常关机(比如突然断电,shutdown abort)之后,这时已经commit的事务已经记录到online redo log中,下次启动数据库时,Oracle进行恢复操作,将online redo log中的事务操作调入内存中,进行相应操作后将数据记入到数据文件中,数据操作完成。

对于没有commit而已经写入数据文件或回退段的数据,也要进行回滚操作,将数据恢复到rollback的状态,使数据文件和控制文件恢复到崩溃前的一致性状态。总之,数据库下次打开时会占用比正常关闭更长的时间。

注意:

并不是所有的异常关机后,下次启动时都可以恢复到正常状态,异常关机容易导致坏块的产生,这种情况下数据库时不能正常启动的,如果处理不当,将会导致大量的数据丢失。

Rolling Forward(前滚)

Oracle启动实例并加载数据库,然后通过Online Redologs中的重做日志,重现实例崩溃前对数据库的修改操作。在恢复过程中对于已经提交的事务,但尚未写入数据文件的那部分数据全部写入数据文件。

Rolling Back(回滚)

Rolling Forward之后,虽然已经提交的修改操作更改的数据都已经被写入数据文件,但在实例崩溃时,部分未提交的事务操作的数据也被写入到数据文件,这些事务必须被撤销。

触发LGWR进程的条件有:

1、用户提交。

2、有1/3重做日志缓冲区未被写入磁盘

3、有大于1M的重做日志缓冲区未被写入磁盘。

4、3秒超时

5、DBWR需要写入的数据的SCN大于LGWR记录的SCN,DBWR触发LGWR写入。

二、SCN(system change number)

SCN是当Oracle数据更新后,由DBMS自动维护去累积递增的一个数字。当一个事务commit时,LGWR会将log buffer写入redo log file,同时也会将该事务的SCN同步写入到redo log flle内(wait -until-completed)。因此当你commit transaction时,在成功的讯息返回之前,LGWR必须先完整的完成上述行为之后,否则你是看不到提交成功的响应讯息。

可以理解的,返回的SCN,也是目前redo log file最新的SCN记录。因为commit后的交易才会有SCN,而且一旦commit就会立刻写入redo log file中。

系统时间标记与SCN之间存在一张表,即SYS下的SMON_SCN_TIME。

SQL> desc sys.smon_scn_time;
Name         Type      Nullable Default                         Comments 
------------ --------- -------- ------------------------------- -------- 
THREAD       NUMBER    Y                                                 
TIME_MP      NUMBER    Y                                                 
TIME_DP      DATE      Y                                                 
SCN_WRP      NUMBER    Y                                                 
SCN_BAS      NUMBER    Y                                                 
NUM_MAPPINGS NUMBER    Y                                                 
TIM_SCN_MAP  RAW(1200) Y                                                 
SCN          NUMBER    Y        0                                        
ORIG_THREAD  NUMBER    Y        0           /* for downgrade */       

每隔5分钟,系统产生一次系统时间标记与SCN的匹配并存入SYS.SMIN_SCN_TIME表(由SMON进程来完成update操作),该表中记录了最近1440个系统时间标记与SCN的匹配记录,由于该表只维护了最近1440条记录,即最近5天内的记录。

查看SCN和timestamp之间的对应关系:

SQL> select scn,to_char(time_dp,'yyyy-mm-dd hh24:mi:ss') from sys.smon_scn_time order by 2;

查询系统当前的SCN:

SQL> select dbms_flashback.get_system_change_number from dual;
GET_SYSTEM_CHANGE_NUMBER
------------------------
                 1052147


SQL> select current_scn from v$database;
CURRENT_SCN
-----------
    1052149

timestamp和SCN互换的2个方法:

SQL> select timestamp_to_scn(to_date('2016-09-08 04:13:09','yyyy-mm-dd hh24:mi:ss')) from dual;
TIMESTAMP_TO_SCN(TO_DATE('2016
------------------------------
                       1052237


SQL> select scn_to_timestamp(1052237) from dual;
SCN_TO_TIMESTAMP(1052237)
--------------------------------------------------------------------------------
08-9月 -16 04.13.09.000000000 上午

三、Checkpoint(检查点)

3.1 检查点定义

大多数关系型数据库都采用“在提交时并不强迫针对数据块的修改完成”而是“提交时保证修改记录(以重做日志的形式)写入日志文件”的机制,来获得性能的优势。即:当用户提交事务,写数据文件是“异步”的,写日志文件是“同步”的。

这就可能导致数据库实例崩溃时,内存中的DB_Buffer中的修改过的数据,坑你没有写入到数据块中。数据库在重新打开时,需要进行恢复,来恢复DB Buffer中的数据状态,并确保已经提交的数据被写入到数据块中。

检查点是这个过程中的重要机制,通过它来确定,恢复时哪些重做日志应该被扫描并应用于恢复。

先看一下Checkpoint queue概念,检查点发生后,触发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写的进度反应到控制文件中,也就是把sbwr当前刚写完的dirty buffer对应的scn写入数据文件头和控制文件,这就是检查点scn。

这个3秒和增量检查点不是一个概念,3秒只是在控制文件中,chpt进程去更新当前dbwr写到哪里了,这个对于ckpt进程来说叫heartbeat,heartbeat是3秒一次,3秒可以看做不停的检查并记录检查点执行情况(DBWR的写进度)。

检查点发生之后数据库的数据文件、控制文件处于一致状态的含义是不需要进行介质恢复,只表示数据文件头一致,但是并不表示数据文件内容一致,因为数据文件内容可能在没有发生检查点的其他情况下的dbwr写数据文件,这样数据文件内容就不一致,若掉电需要进行崩溃恢复。

oracle8以后推出了incremental Checkpoint(增量检查点)的机制,在以前的版本里每个Checkpoint时都会做一个full thread checkpoint(完全检查点),这样的话所有脏数据会被写到磁盘,巨大的I/O对系统性能带来很大影响。

为了解决这个问题,oracle引入了checkpoint queue机制,每一个脏块会被移到检查点队列里面去,按照low rda(第一次对此块修改对应的redo block address)来排列,靠近检查点队列尾端的数据块的low rba值是最小的,而且如果这些脏块被再次修改后它在检查点队列里的顺序也不会改变,这样就保证了越早修改的块越早写入磁盘,每隔3秒钟ckpt会去更新控制文件和数据文件,记录Checkpoint执行的情况。

触发DBWR进程的条件有:

1、DBWR超时,大约3秒。

2、系统中没有多余的空缓冲区来存放数据

3、CKPT进程触发DBWR

Checkpoint有两个目的:

1、确保数据一致性。

2、使数据库能快速地恢复。

Checkpoint期间会有下面进程发生:

1、DBWR写所有脏数据到数据文件;

2、LGWR更新控制文件和数据文件的SCN。

3.2Checkpoints相关优化参数

优化Checkpoint包括4个关键的初始化参数:

fast_start_mttr_target

log_checkpoint_interval

log_checkpoints_to_alert

log_checkpoint_timeout

3.2.1 FAST_START_MTTR_TARGET

FAST_START_MTTR_TARGET参数是调整Checkpoint的首选的方法。FAST_START_MTTR_TARGET可以指定单实例恢复需要的秒数。基于内部的统计,增长的Checkpoint会自动调整的Checkpoint的目标以满足FAST_START_MTTR_TARGET的需求。

v$instance_recovery.ESTIMATED_MTTR显示当前估计需要恢复的秒数,这个值会被显示即使FAST_START_MTTR_TARGET没有被指定。

v$instance_recovery.TARGET_MTTR表名在短时间内MTTR的目标。

v$mttr_target_advice显示这个当前MTTR设置的工作量产生的I/0数量和其他I/O.这个视图帮助用户评定这个在优化和恢复之前的平衡。

3.2.2 LOG_CHECKPOINT_INTERVAL

LOG_CHECKPOINT_INTERVAL参数指定这个最大的重做块的间隔数目。

如果FAST_START_MTTR_TARGET被指定,LOG_CHECKPOINT_INTERVAL不能被设置为0.

LOG_CHECKPOINT_INTERVAL会发生影响当一个Checkpoint发生时,小心设置这些参数,保持它随着重做日志文件大小变化而变化。Checkpoint频繁是整个影响数据库恢复的原因之一。短的Checkpoint间隔意味数据库将 快速恢复,也增加了资源的利用。整个参数也影响数据库向前回滚的时间。实际的恢复时间是基于整个时间,当然还有失败的类型和需要归档日志的数量。

3.2.3 LOG_CHECKPOINT_TIMEOUT

这个参数指定Checkpoint发出的时间间隔。换句话说,它指定一次脏数据多少时间写出一次。Checkpoint频率会影响这个数据库恢复的时间。长时间的间隔会要求数据库恢复要求更久。

Oracle建议用LOG_CHECKPOINT_INTERVAL去控制Checkpoint而不用LOG_CHECKPOINT_TIMEOUT,LOG_CHECKPOINT_TIMEOUT每n秒发生一次Checkpoint,不顾事务提交的频率。这可能会导致一些没有必要的Checkpoint在事务已经发生变化的情况下。不必要的Checkpoint必须被避免。

LOG_CHECKPOINT_TIMEOUT会间隔性地发出log switch。而log switch会触发一个Checkpoint,但Checkpoint不会 导致一个log switch。唯一手工方式alter system switch logfile 或者重新设置redo logs大小可以导致频繁switch。

SQL>alter system set log_checkpoint_timeout=300;[单位是秒]

3.2.4 log_checkpoint_to_alert

设置为true,可以再告警日志中观察到增量检查点的触发

SQL>alter system set log_checkpoints_to_alert=true;

3.3 Checkpoint 触发条件

3.3.1 触发完全检查点 条件

触发完全检查点,促使DBWR将检查点时刻前所有的脏数据写入数据文件。另外,一般正常运行期间的数据库不会产生完全检查点。

1、通过正常事务处理或者立即选项关闭进程时(shutdown immediate或者shutdown normal)

2、当通过设置初始化参数:log_checkpoint_interval,log_checkpoint_timeout,fast_start_io_target 强制时;

3、当数据库管理员手动请求时:alter system Checkpoint;alter tablespace .....offline;

4、每次日志切换时:alter system switch logfile;

注意:

1、alter system switch logile也将触发完全检查点的发生。

2、alter database datafile ....offline不会触发检查点进程。

如果是单纯的offline datafile ,那么讲不会触发文件检查点,只有针对offline tablespace的时候才会触发文件检查点,这也是为什么online datafile需要media recovery 而online tablespace不需要。

对于表空间的offline后再online的这种情况,最好做个强制的Checkpoint比较好。

3.3.2触发增量检查点

1、在联机热备数据文件前,要求该数据文件中被修改的块从DB_Buffer写入数据文件中。所以,发出这样的命令。

alter tablespace tablespace_name begin backup / end backup;也将触发和该表空间的数据文件有关的局部检查点;

2、另外,

alter tablespace tablespace_name read only;

alter tablesapce tablespace_name offline normal;

等命令都会触发增量检查点。

3.4 检查点的一些讨论

3.4.1 commit成功后,数据还会丢失吗?

对于Oracle来说,用户所做的DML操作一旦被提交,则先是在database buffercache中进行修改,同时在修改之前会将数据的前镜像保存在回滚段中, 然后将修改之前和修改之后的数据都写入到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成功了,数据就不会丢失!

3.4.2关于检查点等待事件:

有些事件的产生必须等待检查点的完成,才能开始数据库的其他操作:日志文件切换就是其中一个事件,日志切换必须等待检查点完成。

log file switch(Checkpoint incomplete)这个等待事件本身也说明,日志切换时产生的检查点是需要等待的,这个日志文件所对应脏块全部写完后,检查点进程更新控制文件和数据头,然后这个检查点才能算完成。

也就是说日志切换必须等待检查点完成,而检查点在等待DBWn完成。

这种等待DBWn完成的检查点,最后一步写入控制文件和数据文件头的SCN,肯定是DBWn完成的最后一块的SCN。

3.4.3检查点为什么要等待dbwr完成后才能进行切换(log switch)

log switch时,是不能立即switch到active状态的,log group必须等待。

active表示该log group 还没有完成归档(归档模式下)或者改log group有进行instance recovery的需要用到的日志。所以当Checkpoint发生时,如果dbwr还没有写完它该写完的dirty buffer(该Checkpoint时间点以前产生的dirty buffers,靠scn判断),则该log group处于active状态,不会进行日志切换,当然也不会发生气质文件被覆盖的问题了。

3.4.4如果没有设置archive log,在检查点发生后,发生log switch一个轮回,log file是否会被覆盖掉?

当检查点发生后,会触发lgwr,lgwr会把此时SCN以前在redo buffer中的所有操作写到redo log file,同事lgwr也会触发dbwr进程,dbwr也开始把此刻以前database buffer中的dirty buffer队列中的操作写入datafile。

其实检查点发生后,就是lgwr和dbwr写buffer到磁盘文件的过程,但是两者的读写速度时不同的,dbwr写buffer到数据文件的过程相对较慢,因为dbwr写过程是一个随机读取存储的过程。lgwr写buffer到redo文件的过程比dbwr要快很多,因为lgwe是顺序读取写入的。

由于以上lgwr和dbwr写操作的速度不同,就产生了一个等待问题。既当lgwr轮循一圈后,要进行日志切换,覆盖redo log file,但是此时dbwr还没有写完,那么lgwr就会出现等待,oracle也会hang在那里,同时alter文件中会提示一个相应地提示Checkpoint not complete。等检查点完成后数据库自动恢复正常。

log switch时,会触发检查点,标记检查点时,DBWR要把log file中coverd by the log being Checkpointed的数据写入到数据文件上,如果DBWr没有写完,那么前一个logfile是不能被覆盖的。因此必须等检查点完毕,也可以说是将要被覆盖的日志相关的数据块全部写入数据文件后,该日志文件才能被覆盖!

如果出现这种情况,alter 文件中会记录Checkpoint not complete事件,这通常表示dbwr写入过慢或者logfile组数过少或log fiel太小。

3.4.5检查点发生时,出现日志切换,但是dbwr还没有写完,是否会覆盖redo log file,如果此时掉电,dbwr挂起,会出现丢失数据吗./

会覆盖redo文件,要等待dbwr写完才覆盖。或者说要等待检查点完成才覆盖。

如果DBWn正在写的脏块是指对应到既被覆盖的那个日志文件,那么,因为在这些操作完成之前,不可能有覆盖这件事发生。

如果正在写的脏块是指对应到刚刚被写满的那个日志文件的,肯定一时半会也写不完,日志切换也不会等它,如果切换成功后dbwr挂了,后面的写脏块操作估计也没完成,但是,刚写满的那个文件并没有被覆盖,也不会有任何问题。因此不会出现数据丢失!

数据写入redo log file中,如果在写的过程中,发现一个redo log file 写不下的话,需要写另外一个redo log file,这是应该触发Checkpoint,接着触发DBWn,将脏数据写入datafile,这时发生掉电,导致DBWR失败。这时其实就可以说commit失败了。以上所述其实就是commit没有提交成功。

3.4.6alter system switch logfile 会触发完全检查点;但是为什么,日志却换以后检查点只能记录到上一次归档日志产生的时间呢?而不是现在归档 日志产生的时间呢?

因为这时的Checkpoint Enqueue队列中,也就是脏块队列中脏块还不够多,还不足以触发DBWn写脏块。所以,内存里需要写入的脏块所对应的redo还存在于上一个redo log中,这时你去看该redo log的status 为active。

四、Checkpoint和scn的关系

Checkpoint发生的目的就是要把存储在buffer内的已提交的事务写回disk,否则一旦发生crash,需要进行recovery时,你就必须花很多的时间送redo log file内最后的SCN交易开始进行recovery。

重点在于当commit一个事务时,只会立刻将redo buffer写入redo log file内,但是并不会马上将该update后的block(dirty block)同步写回disk datafile中,这是为了减少过多disk IO的考虑,所以采取batch的方式写入。

在shutdown normal or shutdown immediate下,也就是所谓的clean shutdown,Checkpoint也会自动触发,并且把scn记录写回。

当发生Checkpoint时,会把scn写到四个地方去。三个地方与control file内,一个在datafile header。

(1)System Checkpoint SCN

SQL> select checkpoint_change# from v$database;
CHECKPOINT_CHANGE#
------------------
           6679901

(2)Datafile Checkpoint SCN

SQL> select name,checkpoint_change# from v$datafile where name like '%users01%';
NAME                                                                             CHECKPOINT_CHANGE#
-------------------------------------------------------------------------------- ------------------
/u01/app/oracle/oradata/orcl/users01.dbf                                                    6679901

(3)Stop SCN

SQL> select name,last_change# from v$datafile where name like '%users01%';
NAME                                                                             LAST_CHANGE#
-------------------------------------------------------------------------------- ------------
/u01/app/oracle/oradata/orcl/users01.dbf                                         

正常datafile在read-write mode下list_change#一定是NULL

datafile header中的一个SCN:start SCN在datafile header

五、相关问题:

5.1为什么scn存储在control file中要分为两个地方(system Checkpoint scn,datafile Checkpoint scn)?

当你把一个tablespace 设为read-only时,它的scn会冻结停止,此时datafile Checkpoint scn是不再递增改变的,但是整体的system Checkpoint scn却仍然会不断递增前进。

5.2正常shutdown database后,scn会发生什么变化?

我们可以把数据库开在mount mode

SQL> select checkpoint_change# from v$database;


CHECKPOINT_CHANGE#
------------------
  6788307

SQL> select name,checkpoint_change#,last_change# from v$datafile where name like '%user%';


NAME CHECKPOINT_CHANGE# LAST_CHANGE#
---------------------------------------- ------------------ ------------
/u01/app/oracle/oradata/orcl/users01.dbf    67883076788307

SQL> select name,checkpoint_change# from v$datafile_header where name like '%user%';


NAME CHECKPOINT_CHANGE#
---------------------------------------- ------------------
/u01/app/oracle/oradata/orcl/users01.dbf    6788307

可以看到存储在control file中的三个SCN位置都是相同,注意此时的stop scn不会是null,而是等于start scn

当正常shutdown时,Checkpoint会进行,并且此时datafile的stop scn和start scn会相同,等到开启数据库时,Oracle检查datafile header中的start scn和存在于control file中的datafile的scn是否相同,如果相同,接着检查start scn和stop scn是否相同,如果仍然相同,数据库就会正常开启,否则就需要recovery 等到数据库开启后,存储在control file中的stop scn就会恢复为null值,此时datafile是open在正常模式。

如果不正常关闭shutdown(shutdown abort)则mount数据库后,你会发现stop scn并不是等于其他位置的scn,而是等于null,这表示oracle在shutdown时没有进行Checkpoint,下次开机必须进行crash recovery。

crash recovery

必须先进行roll Forward(从redo log file中从目前的start scn开始,重做后面的已提交的交易)。在从roll back segment做rollback未完成(dead transaction)交易。检验controlfile中的SCN会等于datafile header的scn。

5.3 crash recovery和media recovery的比较

启动数据库时,如果发现stop scn=NULL,表示需要进行creash recovery;

启动数据库时,如果发现有datafile header的start scn不等于存储于controlfile的datafile scn 表示需要进行media recovery

5.4 recovery database两种常见问题

(1)recover database umtil cancel ====》open database resetlog

这种情况下datafile header中的scn一定小于controlfile中的datafile scn。

如果你有进行restoredatafile,则该restore的datafile header scn一定会小于目前的controlfile的datafile scn,此时会无法开启数据库,必须进行media recovery。重做archive log直到该datafile header的scn=Current scn

restore datafile后,可以mount database 然后去检查controlfile and datafile header的scn:

(2)recover database until cancel using backup controlfile;=======> open database resetlog

这种情况下,datafile header 中的scn一定会大于controlfile的datafile scn。因为控制文件是旧的控制文件。所以它保存的信息也是以前的信息。

如果只是某table被drop掉,没有破坏数据库整体数据结构,还可以用ncomplete recovery解决:

如果是某个tablespace or datafile 被drop掉,因为档案结构已经破坏,目前的control file内已经没有改datafile的信息,就算你只有restore datafile 然后进行incomplete recovery也无法挽回被drop的datafile。

只好restore之前备份的control file(里头被drop datafile metadata此时还存在),不过resetore control file后,此时Oracle会被发现controlfile内的system scn会小于目前的datafile header scn,也不等于目前存储于log file内的scn,此时就必须使用recover database until using backup controlfile到drop datafile or drop tablespace 之前的scn。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值