一. Redo log 作用
数据库异常关机(比如突然断电,shutdown abort: 它会立即关闭数据库,等同于断电)之后,这时已经commit的事务已经记录到online redo log中,下次启动数据库时,Oracle进行恢复操作,将online redo log中的事务操作调入内存中,进行相应操作后将数据记入到数据文件中,数据操作完成。对于没有commit而已经写入数据文件或回退段的数据,也要进行回滚操作,将数据恢复到rollback的状态,使数据文件和控制文件恢复到崩溃前的一致性状态。总之,数据库下次打开时会占用比正常关闭更长的时间。
注意:并不是所有异常关机后,下次启动时都可以恢复到正常状态,异常关机容易导致坏块的产生,这种情况下数据库是不能正常启动的,如果处理不当,将会导致大量数据的丢失。 具体参考我的blog:
Oracle 坏块 总结
http://blog.csdn.net/tianlesoftware/archive/2009/12/18/5024966.aspx
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写入。
二. Checkpint(检查点)
2.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 写的进度反应到控制文件中,也就是把dbwr当前刚写完的dirty buffer对应的scn 写入数据文件头和控制文件,这就是检查点scn。
这个3秒和增量检查点不是一个概念,3秒只是在控制文件中,ckpt 进程去更新当前 dbwr写到哪里了,这个对于 ckpt 进程来说叫 heartbeat ,heartbeat是3秒一次,3秒可以看作不停的检查并记录检查点执行情况(DBWR的写进度)。
检查点发生之后数据库的数据文件、控制文件处于一致状态的含义是不需要进行 介质恢复,只表示数据文件头一致,但是并不表示数据文件内容一致,因为数据文件内容可能在没有发生检查点的其它情况下的dbwr写数据文件,这样数据文件内容就不一致,若掉电需要进行崩溃恢复。
触发DBWR进程的条件有:
1. DBWR超时,大约3秒
2. 系统中没有多余的空缓冲区来存放数据
3. CKPT 进程触发DBWR
2.2 Checkpoint 触发条件
oracle8以后推出了incremental checkpoint(增量检查点)的机制,在以前的版本里每checkpoint时都会做一个full thread checkpoint(完全检查点),这样的话所有脏数据会被写到磁盘,巨大的i/o对系统性能带来很大影响。为了解决这个问题,oracle引入了checkpoint queue机制,每一个脏块会被移到检查点队列里面去,按照low rdb(第一次对此块修改对应的redo block address)来排列,靠近检查点队列尾端的数据块的low rba值是最小的,而且如果这些赃块被再次修改后它在检查点队列里的顺序也不会改变,这样就保证了越早修改的块越早写入磁盘。每隔3秒钟ckpt会去更新控制文件和数据文件,记录checkpoint执行的情况。
触发CheckPoint(检查点) 条件有很多,比如:
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 logfile也将触发完全检查点的发生。
2. alter database datafile ... offline 不会触发检查点进程。
如果是单纯的offline datafile,那么将不会触发文件检查点,只有针对offline tablespace的时候才会触发文件检查点,这也是为什么online datafile需要media recovery而online tablespace不需要。
对于表空间的offline后再online这种情况,最好做个强制的checkpoint比较好。
关于offline datafile 和tablespace 的区别也可以参考我的blog:
ALTER DATABASE 与 ALTER TABLESPACE OFFLINE的区别
http://blog.csdn.net/tianlesoftware/archive/2009/11/30/4898800.aspx
以上的触发条件将触发完全检查点,促使DBWR 将检查点时刻前所有的脏数据写入数据文件。另外,一般正常运行期间的数据库不会产生完全检查点。
下面很多事件将导致增量检查点,比如:
1. 在联机热备份数据文件前,要求该数据文件中被修改的块从DB_Buffer 写入数据文件中。所以,发出这样的命令:
ALTER TABLESPACE tablespace_name BIGEN BACKUP & end backup; 也将触发和该表空间的数据文件有关的局部检查点;
2. 另外,
ALTER TABLESPACE tablespace_name READ ONLY;
ALTER TABLESPACE tablespace_name OFFLINE NORMAL;
等命令都会触发增量检查点。
2.3 关于检查点的一点具体应用讨论
2.3.1. 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成功的了,数据不会丢失!
2.3.2. 数据库发生一次DBWn,是否将所有buffer cache 中的dirty buffer 都写入,还是先将脏队列中的数据写入?
这话看起来有道理,但实际上,dbwr在写的时候又不断地在产生dirty buffer ,所以说检查点发生的时候是期望把该时间点之前的所有脏缓冲区写入数据文件。
所有的buffer,不在LRU list上就在dirty list上, dbwr写入的时候,一次写的块数是有一个批量写的隐藏参数控制的。
所以说要是 dbwr将 dirty list也好, lru list上的也好,要实现全部写入,都是一个现实系统中很难存在的现象。dirty 总是在不断的产生,dbwr总是在不断地写,增量检查点发生的时候也并不意味着一定要更新数据文件头,检查点开始的时候只表示该次检查点结束的时候要更新数据文件头的话数据文件头具有该时间点的一致性。
2.3.3. 关于检查点等待事件:
有些事件的产生必须等待检查点的完成,才能开始数据库的其它操作:日志文件切换就是其中一个事件,日志切换必须等待检查点完成。
log file switch (checkpoint incomplete) 这个等待事件本身也说明,日志切换时产生的检查点是需要等待的,这个日志文件所对应脏块全部写完后,检查点进程更新控制文件和数据头,然后这个检查点才能算完成。
也就是说日志切换必须等待检查点完成,而检查点在等待DBWn完成。
这种等待DBWn完成的检查点,最后一步写入控制文件和数据文件头的SCN,肯定是DBWn完成的最后一块的SCN。
2.3.4. 检查点为什么要等待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状态,不会进行日志切换,当然也不会发生日志文件被覆盖的问题了。
2.3.5. 如果没有设置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太小。
Redo Log 和Checkpoint not complete
http://blog.csdn.net/tianlesoftware/archive/2009/12/01/4908066.aspx
2.3.6. 检查点发生时,出现日志切换,但是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没有提交成功。
2.3.7. alter systen switch logfile会触发完全检查点;但是为什么,日志切换以后检查点只能记录到上一次归档日志产生的时间呢?而不是现在归档日志产生的时间呢?
因为这时的checkpoint enqueue队列中,也就是脏块队列中脏块还不够多,还不足以触发DBWn写脏块。所以,内存里需要写入的脏块所对应的redo还存在于上一个redo log里,这时你去看该redo log的status为active。
我们可以通过一下语句查看相关信息:
SQL> select * from x$kccrt;
三. SCN(system change number)
3.1 SCN 定义:
SCN是当Oracle数据更新后,由DBMS自动维护去累积递增的一个数字。 当一个事务commit时,LGWR会将log buffer写入redo log file,同时也会将该事务的 SCN同步写入到redo log file内(wait-until-completed)。因此当你commit transaction时, 在成功的讯息返回之前,LGWR必须先完整的完成上述行为之后,否则你是看不到提交成功的响应讯息。
我们可以查询目前系统最新的SCN
select dbms_flashback.get_system_change_number from dual;
可以理解的,这里返回的SCN,也是目前redo log file最新的SCN纪录。 因为commit后的交易才会有SCN,而一旦commit就会立刻写入redo log file中。
3.2 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的方式写入。
When a checkpoint occurs, Oracle must update the headers of all datafiles to record the details of the checkpoint. This is done by the CKPT process. The CKPT process does not write blocks to disk; DBWn always performs that work.
在shutdown normal or shutdown immediate下,也就是所谓的clean shutdown,checkpoint也会自动触发,并且把SCN纪录写回。
当发生checkpoint时,会把SCN写到四个地方去。
三个地方于control file内,一个在datafile header。
Control file三个地方为
1.System checkpoint SCN ===========> (SYSTEM CHECKPOINT SCN in control file)
SQL> select checkpoint_change# from v$database;
CHECKPOINT_CHANGE#
--------------------
292767
2.Datafile checkpoint SCN ===============> (DATAFILE CHECKPOINT SCN in control file)
SQL> select name,checkpoint_change#
from v$datafile where name like '%users01%';
NAME CHECKPOINT_CHANGE#
----------------------------------- --------------------
/u02/oradata/OMFD1/users01.dbf 292767
3.Stop SCN ======================> (STOP SCN in control file)
SQL> select name,last_change#
from v$datafile where name like '%users01%';
NAME LAST_CHANGE#
----------------------------------- ------------
/u02/oradata/OMFD1/users01.dbf
正常datafile在read-write mode下 last_change#一定是NULL
另外一个地方在datafile header内
4.Start SCN ================================> (DATAFILE HEADER)
SQL> select name,checkpoint_change#
from v$datafile_header where name like '%users01%';
NAME CHECKPOINT_CHANGE#
----------------------------------- --------------------
/u02/oradata/OMFD1/users01.dbf 292767
3.3 相关问题
3.3.1 为什么储存在CONTROL FILE中要分为两个地方(SYSTEM CHECKPOINT SCN,DATAFILE CHECKPOINT SCN) ?
当你把一个tbs设为read-only时,他的SCN会冻结停止,此时DATAFILE CHECKPOINT SCN是不会再递增改变的, 但是整体的SYSTEM CHECKPOINT SCN却仍然会不断递增前进。
所以,这就是为什么需要分别在两个地方储存SCN。
3.3.2 正常shutdown database后,SCN会发生什么变化?
我们可以把数据库开在mount mode
select checkpoint_change# from v$database;
CHECKPOINT_CHANGE#
--------------------
293184
select name,checkpoint_change#,last_change# from v$datafile where name like '%user%';
NAME CHECKPOINT_CHANGE# LAST_CHANGE#
----------------------------------- -------------------- --------------
/u02/oradata/OMFD1/users01.dbf 293184 293184
可以看到储存在control file中的三个SCN位置都是相同,注意此时的stop scn不会是NULL,而是等于start scn
我们来查询datafile header SCN:
select name,checkpoint_change# from v$datafile_header where name like '%users01%';
NAME CHECKPOINT_CHANGE#
----------------------------------- --------------------
/u02/oradata/OMFD1/users01.dbf 293184
当clean 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
select 'controlfile' "SCN location",name,checkpoint_change#
from v$datafile where name like '%users01%'
union
select 'file header',name,checkpoint_change#
from v$datafile_header where name like '%users01%';
SCN location NAME CHECKPOINT_CHANGE#
-------------- ----------------------------------- --------------------
controlfile /u02/oradata/OMFD1/users01.dbf 293188
file header /u02/oradata/OMFD1/users01.dbf 293188
3.3.3 crash recovery 和media recovery 的比较
启动数据库时,如果发现STOP SCN = NULL,表示需要进行crash recovery;启动数据库时,如果发现有datafile header的START SCN 不等于储存于CONTROLFILE的DATAFILE SCN,表示需要进行Media recovery
STOP SCN equal NULL ==> NEED CRASH RECOVERY
DATAFILE HEADER START SCN not equal CONTROLFILE SCN ==> NEED MEDIA RECOVERY
3.3.4 RECOVERY DATABASE 两种常见问题
1) RECOVER DATABASE UNTIL CANCEL ==> OPEN DATABASE RESETLOG
==> DATAFILE HEADER SCN一定会小于CONTROLFILE的DATAFILE SCN
如果你有进行RESTORE DATAFILE,则该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
select 'controlfile' "SCN location",name,checkpoint_change#
from v$datafile where name like '%users01%'
union
select 'file header',name,checkpoint_change#
from v$datafile_header where name like '%users01%';
SCN location NAME CHECKPOINT_CHANGE#
-------------- ----------------------------------- --------------------
controlfile /u02/oradata/OMFD1/users01.dbf 313551
file header /u02/oradata/OMFD1/users01.dbf 313401
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的DATA FILE。
只好RESOTRE 之前备份的CONTROL FILE(里头被DROP DATAFILE Metadata此时还存在),不过RESTOREC CONTROL FILE后 此时Oracle会发现CONTROL FILE内的SYSTEM SCN会小于目前的DATAFILE HEADER SCN,也不等于目前储存于LOG FILE内的SCN, 此时就必须使用RECOVER DATABASE UNTIL CANCEL USING BACKUP CONTROLFILE到DROP DATAFILE OR DROP TABLESPACE之前的SCN。
另一种特殊状况就是,万一不幸地所有CONTROL FILE都遗失了,也必须用这种方式救回,所以请做MULTIPLEXING。