深入了解Oracle SCN (1)
--------------------------------------------------------------------------------
[说明]本来在研究Backup andRecovery,可是在做实验的时候遇到一些问题不是很理解,进而追根求源,想搞清楚,于是必须搞清楚SCN和Recovery之间的关系。于是乎转而研究SCN,这两天看了很多文章,包括Eygle网站上的文章,itpub上biti等大师以前发过或者参与讨论过的帖子,发现思路有模糊到清晰,再由清晰到模糊,越走越深,至今还没有完全搞清楚。决定暂时到此为止,等以后各方面的知识积累多了,再转过来研究一下。本想自己写个总结性的文章,可是发现了一篇文章已经写的很好了,我就懒惰的以逸待劳,copy过来了,在此谢谢前辈们的工作,大家辛苦了~
SCN的概念
SCN是顺序递增的一个数字,在Oracle中用来标识数据库的每一次改动,及其先后顺序。SCN的最大值是0xffff.ffffffff。
SCN的管理方式
Oracle对SCN的管理,分为单节点和RAC两种方式。
单节点的instance中
单节点的instance中,SCN值存在SGA区,由system commit number latch保护。任何进程要得到当前的SCN值,都要先得到这个latch。
RAC/OPS环境中
Oracle通过排队机制(Enqueue)实现SCN在各并行节点之间的顺序增长。具体有两种方法:
Lamport算法:又称面包房算法,先来先服务算法。跟很多银行采用的排队机制一样。客户到了银行,先领取一个服务号。一旦某个窗口出现空闲,拥有最小服务号的客户就可以去空闲窗口办理业务。
Commit广播算法:一有commit完成,最新的SCN就广播到所有节点中。
上述两种算法可以通过调整初始化参数max_commit_propagation_delay来切换。在多数系统(除了Compaq Tur64Unix)中,该参数的默认值都是700厘秒(centisecond),采用Lamport算法。如果该值小于100厘秒,Oracle就采用广播算法,并且记录在alert.log文件中。
几种重要的SCN
Commit SCN
当用户提交commit命令后,系统将当前scn赋给该transaction。这些信息都反映在redo buffer中,并马上更新到redo log文件里。
Offline SCN
除了System tablespace以外的任何表空间,当我们执行SQL>alter tablespace…offlinenormal命令时,就会触发一个checkpoint,将内存中的dirtybuffer写入磁盘文件中。Checkpoint完成后,数据文件头会更新checkpoint scn和offline normalscn值。其中数据库文件头的checkpoint scn值可通过查询列x$kccfe.fecps得到。
如果执行SQL>altertablespace…offline命令时采用temporary或immediate选项,而不用normal选项时,offlinenormal scn会被设成0。这样当数据库重启后通过resetlog方式打开时,该表空间就无法再改回在线状态。
Checkpoint SCN
当数据库内存的脏数据块(dirty blocks)写到各数据文件中时,就发生一次checkpoint。数据库的当前checkpointscn值存在x$kccdi.discn中。Checkpointscn在数据库恢复中起着至关重要的作用。无论你用何种办法恢复数据库,只有当各个数据库文件的checkpoint scn都相同时,数据库才能打开。
虽然参数“_allow_resetlogs_corruption”可以在checkpoint scn不一致时强制打开数据库,但是这样的数据库在open后必须马上作全库的export,然后重建数据库并import数据。
Resetlog SCN
数据库不完全恢复时,在指定时间点后的scn都无法再应用到数据库中。Resetlog时的scn就被设成当前数据库scn,redo log也会被重新设置。
Stop SCN
Stopscn记录在数据文件头上。当数据库处在打开状态时,stop scn被设成最大值0xffff.ffffffff。在数据库正常关闭过程中,stopscn被设置成当前系统的最大scn值。在数据库打开过程中,Oracle会比较各文件的stop scn和checkpointscn,如果值不一致,表明数据库先前没有正常关闭,需要做恢复。
High and Low SCN
Oracle的Redo log会顺序纪录数据库的各个变化。一组redo log文件写满后,会自动切换到下一组redo log文件。则上一组redo log的high scn就是下一组redo log的low scn。
在视图v$log_history中,sequence#代表redo log的序列号,first_change#表示当前redo log的low scn,列next_change#表示当前redo log的high scn。
SQL> col recid format 9999
SQL> col requence# format 9999
SQL> col first_change# format 9,999,999,999,999
SQL> col next_change# format 9,999,999,999,999
SQL> select recid,sequence#,first_change#,next_change# from v$log_history where rownum<6;
RECID SEQUENCE# FIRST_CHANGE# NEXT_CHANGE#
----- ---------- ------------------ ------------------
484 484 1,928,645,840,091 1,928,645,840,436
485 485 1,928,645,840,436 1,928,645,840,636
486 486 1,928,645,840,636 1,928,778,045,209
487 487 1,928,778,045,209 1,929,255,480,725
488 488 1,929,255,480,725 1,930,752,214,033
SCN号与oracle数据库恢复的关系
SCN号与oracle数据库恢复过程有着密切的关系,只有很好地理解了这层关系,才能深刻地理解恢复的原理,从而才能很好地解决这方面的问题。
SCN与CHECKPOINT
CKPT进程在checkpoint发生时,将当时的SCN号写入数据文件头和控制文件,同时通知DBWR进程将数据块写到数据文件。
CKPT进程也会在控制文件中记录RBA(redo byte address),以标志Recovery需要从日志中哪个地方开始。与checkpoint相关的SCN号有四个,其中三个存在控制文件中,一个存放在数据文件头中。
这四个分别是:
1.System Checkpoint SCN
当checkpoint完成后,ORACLE将System Checkpoint SCN号存放在控制文件中。我们可以通过下面SQL语句查询:
select checkpoint_change# from v$database;
2.Datafile Checkpoint SCN
当checkpoint完成后,ORACLE将Datafile Checkpoint SCN号存放在控制文件中。我们可以通过下面SQL语句查询所有数据文件的Datafile Checkpoinnt SCN号。
select name,checkpoint_change# from v$datafile;
3.Start SCN号
ORACLE将Start SCN号存放在数据文件头中。
这个SCN用于检查数据库启动过程是否需要做Media Recovery.
我们可以通过以下SQL语句查询:
select name,checkpoint_change# from v$datafile_header;
4.End SCN (Stop SCN)号
ORACLE将End SCN号存放在控制文件中。
这个SCN号用于检查数据库启动过程是否需要做Instance Recovery.
我们可以通过以下SQL语句查询:
select name,last_change# from v$datafile;
在数据库正常运行的情况下,对可读写的,online的数据文件,该SCN号为NULL.
我们作个小的试验,内容如下:
在执行检查点进程之前SCN号如下:
System Checkpoint SCN 4609061
--select checkpoint_change# from v$database;
Datafile Checkpoint SCN 4609061
--select name,checkpoint_change# from v$datafile;
Start SCN 4609061
--select name,checkpoint_change# from v$datafile_header;
End SCN空
--select name,last_change# from v$datafile;
执行alter system checkpoint。后的SCN号如下:
System Checkpoint SCN 4609630
--select checkpoint_change# from v$database;
Datafile Checkpoint SCN 4609630
--select name,checkpoint_change# from v$datafile;
Start SCN 4609630
--select name,checkpoint_change# from v$datafile_header;
End SCN null
--select name,last_change# from v$datafile;
SCN不连续原因可能如下:
1.当发生日志组切换的时候
2.当符合LOG_CHECKPOINT_TIMEOUT,LOG_CHECKPOINT_INTERVAL,fast_start_io_target,fast_start_mttr_target参数设置的时候
3.当运行ALTER SYSTEM SWITCH LOGFILE的时候
4.当运行ALTER SYSTEM CHECKPOINT的时候
5.当运行alter tablespace XXX begin backup,end backup的时候
6.当运行alter tablespace ,datafile offline的时候;
SCN号与数据库启动
在数据库启动过程中,当System Checkpoint SCN、Datafile Checkpoint SCN和StartSCN号都相同时,数据库可以正常启动,不需要做media recovery.三者当中有一个不同时,则需要做mediarecovery。如果在启动的过程中,End SCN号为NULL,则需要做instancerecovery。ORACLE在启动过程中首先检查是否需要media recovery,然后再检查是否需要instance recovery。
SCN号与数据库关闭
如果数据库的正常关闭的话,将会触发一个checkpoint,同时将数据文件的END SCN号设置为相应数据文件的Start SCN号。
当数据库启动时,发现它们是一致的,则不需要做instance recovery。在数据库正常启动后,ORACLE会将END SCN号设置为NULL。如果数据库异常关闭的话,则END SCN号将为NULL.
为什么需要System checkpoint SCN号与Datafile Checkpoint SCN号
为什么ORACLE会在控制文件中记录System checkpoint SCN号的同时,还需要为每个数据文件记录
Datafile Checkpoint SCN号?
原因有二:
1.对只读表空间,其数据文件的Datafile Checkpoint SCN、Start SCN和END SCN号均相同。
这三个SCN在表空间处于只读期间都将被冻结。
2.如果控制文件不是当前的控制文件,则System checkpoint会小于Start SCN或END SCN号。记录这些SCN号,可以区分控制文件是否是当前的控制文件。
Recovery database using backup controlfile
当有一个Start SCN号超过了System CheckpoitSCN号时,则说明控制文件不是当前的控制文件,因此在做recovery时需要采用using backupcontrolfile。这是为什么需要记录SystemCheckpoint SCN的原因之一。
这里需要一提的是,当重建控制文件的时候,System Checkpoint SCN为0,Datafile Checkpoint SCN的数据来自于StartSCN。根据上述的描述,此时需要采用using backup controlfile做recovery。
重建控制文件,重建方式分两种(resetlogs和noresetlogs)(此段内容来自:http://space.51CTO提醒您,请勿滥发广告!/12361284/viewspace-346)
1.使用resetlogs选项时,System Checkpoint SCN为被归为0,而其中记录的各个数据文件的DatafileCheckpoint SCN则来自于StartSCN(也就是说可能会从冷备份的数据文件的数据文件头中获取)。根据上述的描述,此时需要采用using backupcontrolfile做recovery.因此情况是System Checkpoint SCN=0 < Start SCN =Datafile Checkpoint SCN。
2.使用noresetlogs选项时,有一个前提就是:一定要有online redolog的存在。否则就要使用resetlogs选项。这个时候控制文件重建好时,其system checkpoint SCN=DatafileCheckpoint SCN=Lastest Checkpoint SCN in online redo log,我们可以看到DatafileCheckpoint SCN并没有从StartSCN中读取。而是读取了最新的日志文件中的SCN作为自己的数据。此时重建的控制文件在恢复中的作用跟最新的控制文件类似,SystemCheckpoint SCN(已经读取最新的redo log的checkpoint SCN信息)可能会>StartSCN(因为数据文件可能会从冷备份中恢复),恢复时就不需要加using backup controlfile子句了。
关于backupcontrolfile的补充:backup controlfile只有备份时刻的archive log信息,并没有DBcrash时刻的archive log信息,所以并不会自动应用online redo log,而是提示找不到序号为LastestArchive log sequence + 1的archive log,尽管你可以手动指定online redolog来实现完全恢复,但因为一旦使用了using backup controlfile子句,Oracle就视为不完全恢复,必须openresetlogs!实际上,假如你有旧的控制文件又不想resetlogs,那很简单,使用旧的控制文件mount然后backup totrace,然后手工创建控制文件,使用reuse database ... noresetlogs .这样就可以recoverdatabase自动恢复并open database而不用resetlogs了(记住:必须有所有的online redologs才可以这样!)。
备份的控制文件不能自动进行完全恢复
可以手工apply日志进行完全恢复
重新创建的可以自动进行完全恢复(By biti)
示例
例子背景:
oracle 8i
windows
采用rman做热备,在备份期间,做不少事务,同时做alter system checkpoint.
RMAN> run {
2> allocate channel c1 type disk;
3> backup database filesperset 3 format 'e:/full_%p_%t.bak';
4> }
(这里需要一提的是,在这个备份角本里面我们加了filesperset3。这样将整个数据库分成两个备份集。这样还原出来的数据文件其checkpoint_change#将不一样。否则由于数据库数据文件不多,都将包含在一个备份集中,这样即使在备份中做insert操作和alter systemcheckpoint也不会产生不同的checkpoint_change#。因为rman备份是将一个备份集中的文件同时备份的。
而checkpoint_change#是存放在数据文件头部的,这样备份这些数据文件的头部的时间将是很快的。)
然后
RMAN> run{
2> allocate channel c1 type disk;
3> restore database;
4> }
SQL> select checkpoint_change# from v$database;
CHECKPOINT_CHANGE#
------------------
2156662354
SQL> select file#,checkpoint_change# from v$datafile;
FILE# CHECKPOINT_CHANGE#
---------- ------------------
1 2156662355
2 2156662354
3 2156662322
4 2156662354
5 2156662354
6 2156662354
SQL> select file#,checkpoint_change# from v$datafile_header;
FILE# CHECKPOINT_CHANGE# LAST_CHANGE#
---------- ------------------ -------------
1 2156662355
2 2156662349
3 2156662322
4 2156662342
5 2156662349
6 2156662342
从这里可以看出,显然是需要做media recovery的。正常情况下,还需要做instance recovery.当然由于没有在线日志,所以只能做resetlogs。
1.有归档日志存
若有归档日志在,则只需要做一个recover database until cancel;
然后即可alter database open resetlogs;
SQL> recover database until cancel (using backup controlfile);
ORA-00279: change 2156621770 generated at 10/07/2005 14:30:06 needed for thread 1
ORA-00289: suggestion : D:ORACLE8IRDBMSARC00738.001
ORA-00280: change 2156621770 for thread 1 is in sequence #738
Specify log: {=suggested | filename | AUTO | CANCEL}
auto
ORA-00279: change 2156621779 generated at 10/07/2005 14:30:51 needed for thread
1
ORA-00289: suggestion : D:ORACLE8IRDBMSARC00739.001
ORA-00280: change 2156621779 for thread 1 is in sequence #739
ORA-00278: log file 'D:ORACLE8IRDBMSARC00738.001' no longer needed for this recovery
ORA-00308: cannot open archived log 'D:ORACLE8IRDBMSARC00739.001'
ORA-27041: unable to open file
OSD-04002: unable to open file
O/S-Error: (OS 2) 系统找不到指定的文件。
SQL> alter database open resetlogs;
Database altered.
2.无归档日志
如果没有归档日志,由于restore出来是没有在线日志的。
如果v$datafile_header中checkpoint_change#是相同的,此时由于控制文件中checkpoint_change#比数据文件头中要高,所以数据库还是需要做media recovery。
此时重建控制文件还是一样的,因为重建控制文件后,在控制文件中checkpoint_change#为0,与文件头的checkpoint_change#还是不一样,还需要mediarecovery.且由于控制文件中checkpoint_change#比文件头中要高,所以做recover时还需要加上using backupcontrolfile.
注意,这时由于没有在线日志,所以重建控制文件需要将noresetlogs改成RESETLOGS才可以创建成功,否则会报以下错误:
ORA-01565: error in identifying file 'D:ORACLE8IORADATAORA8IREDO01.LOG'
ORA-27041: unable to open file
如:
CREATE CONTROLFILE REUSE DATABASE "ORA8I" RESETLOGS ARCHIVELOG
MAXLOGFILES 32
MAXLOGMEMBERS 2
MAXDATAFILES 254
MAXINSTANCES 1
MAXLOGHISTORY 226
LOGFILE
GROUP 1 'D:ORACLE8IORADATAORA8IREDO01.LOG' SIZE 1M,
GROUP 2 'D:ORACLE8IORADATAORA8IREDO02.LOG' SIZE 1M,
GROUP 3 'D:ORACLE8IORADATAORA8IREDO03.LOG' SIZE 1M
DATAFILE
'D:ORACLE8IORADATAORA8ISYSTEM01.DBF',
'D:ORACLE8IORADATAORA8IRBS01.DBF',
'D:ORACLE8IORADATAORA8IUSERS01.DBF',
'D:ORACLE8IORADATAORA8ITEMP01.DBF',
'D:ORACLE8IORADATAORA8ITOOLS01.DBF',
'D:ORACLE8IORADATAORA8IINDX01.DBF'
CHARACTER SET ZHS16GBK
;
此时scn号信息如下:
SQL> select CHECKPOINT_CHANGE#,CONTROLFILE_CHANGE# from v$database;
CHECKPOINT_CHANGE# CONTROLFILE_CHANGE#
------------------ -------------------
0 0
此时由于没有归档日志和在线日志,无法做recovery。
SQL> recover database using backup controlfile until cancel;
ORA-00279: change 2156662342 generated at 10/07/2005 17:06:27 needed for thread 1
ORA-00289: suggestion : D:ORACLE8IRDBMSARC00749.001
ORA-00280: change 2156662342 for thread 1 is in sequence #749
Specify log: {=suggested | filename | AUTO | CANCEL}
cancel
ORA-01547: warning: RECOVER succeeded but OPEN RESETLOGS would get error below
ORA-01152: file 1 was not restored from a sufficiently old backup
ORA-01110: data file 1: 'D:ORACLE8IORADATAORA8ISYSTEM01.DBF'
ORA-01112: media recovery not started
所以也就无法做alter database open Resetlogs了。
此时可以加上_allow_resetlogs_corruption隐含参数,然后就可以alter database open resetlogs将数据库打开了。
当然如果v$datafile_header中checkpoint_change#是不相同的,那么此时就没有什么常归有效的办法能将数据库打开了。
如果相差不多,加上隐含参数_allow_resetlogs_corruption,然后alter database openresetlogs还是有可能可以打开的。这个参数oracle是不建议加的,且加上这个参数也只是有可能可以打开。这个参数是以最oldest的scn将数据库打开,所以最好system数据文件的scn号是最oldest的,否则容易产生大量的600号错误。