SCN(system change number)是oracle中很重要的一个机制,在数据恢复,data guard,streams复制,rac节点同步等各个功能中起到重要的作用。
我们首先看下oracle事务中数据的变化时怎么写入数据文件的:
1.事务开始;
2.在buffer cache中找到需要的块,如果没有找到的话,则从数据文件中加载入buffer cache;
3.事务修改了buffer cache中的数据,该数据被标示为“脏数据”dirty data,同时写入log buffer;
4.事务提交,LGWR进程将log buffer中的脏数据写入redo log file中;
5.当发生checkpoint,ckpt进程更新所有数据文件文件头的信息,DBWN进程负责将buffer cache中的脏数据写入数据文件。
经过上面的5个步骤,事务中变化的数据被写入数据文件中,但是如果中间发生了意外情况,如数据库服务器宕机了,我们就不知道哪些数据已经写入数据文件里了。
scn机制就是解决上述的问题。scn是一个只增不减的数字。在oracle中有4类scn:
1、系统检查点scn
当一个检查点动作完成之后,Oracle就把系统检查点的SCN存储到控制文件中。
select checkpoint_change# from v$database
2、数据文件检查点scn
当一个检查点动作完成之后,Oracle就把每个数据文件的scn单独存放在控制文件
中。
select name,checkpoint_change# from v$datafile
3、启动scn
Oracle把这个检查点的scn存储在每个数据文件的文件头中,这个值称为启动scn,
因为它用于在数据库实例启动时,检查是否需要执行数据库恢复。
select name,checkpoint_change# from v$datafile_header
4、终止scn
每个数据文件的终止scn都存储在控制文件中。
select name,last_change# from v$datafile
在正常的数据库操作过程中,所有正处于联机读写模式下的数据文件的终止scn都为null.
其中前面三个scn被存在控制文件中,开始scn被存储在数据文件中。在控制文件中,系统检查点scn是针对整个数据库的,故只存在一个;而数据文件检查点和结束scn是针对每个数据文件的,因而针对每个数据文件,在控制文件中就应该相应的存在一粉数据文件检查点scn和结束scn。在数据库正常运行期间,stop scn是个无穷大的数字或者null。
SQL> select last_change# from v$datafile;
LAST_CHANGE#
------------
6 rows selected.
在一个事务提交了之后,会在redo log中存在一条redo记录,同时,系统为其提供一个scn,记录在该记录中。如果该条记录是在redo log被清空时(日志满了或者发生了checkpoint,所有变化日志已经被写入数据文件中),则scn被记录成redo log的low scn。以后再日志被清空之前写入redo log的记录scn为next scn。
当日志切换或发生checkpoint时,从low scn到next scn之间的所有redo记录的数据被DBWn写入数据文件,而ckpt进程则将所有的数据文件文件头的start scn,控制文件的system checkpoint scn,每个数据文件对应的datafile checkpoint也更新为next scn。如果该数据文件所在的表空间是只读的话,数据文件文件头的start scn和控制文件中datafile checkpoint不会被更新。
我们看下日志文件的情况:
SQL> select * from v$log;
GROUP# THREAD# SEQUENCE# BYTES MEMBERS ARC STATUS
---------- ---------- ---------- ---------- ---------- --- ----------------
FIRST_CHANGE# FIRST_TIME
------------- ------------
1 1 13 52428800 1 YES INACTIVE
927180 10-JUL-09
2 1 15 52428800 1 NO CURRENT
991804 13-JUL-09
3 1 14 52428800 1 YES INACTIVE
966433 12-JUL-09
我们可以看到,当前的日志文件是2,first_change是991804(F223C)。我们在看一下日志文件2的转储
SQL> alter system dump logfile '/export/home/oracle/oradata/uep/redo02.log';
System altered.
打开转储出来的文件,可以看到
DUMP OF REDO FROM FILE '/export/home/oracle/oradata/uep/redo02.log'
Opcodes *.*
RBAs: 0x000000.00000000.0000 thru 0xffffffff.ffffffff.ffff
SCNs: scn: 0x0000.00000000 thru scn: 0xffff.ffffffff
Times: creation thru eternity
FILE HEADER:
Compatibility Vsn = 169869824=0xa200200
Db ID=3312617616=0xc5728890, Db Name='UEP'
Activation ID=3312619408=0xc5728f90
Control Seq=701=0x2bd, File size=102400=0x19000
File Number=2, Blksiz=512, File Type=2 LOG
descrip:"Thread 0001, Seq# 0000000015, SCN 0x0000000f223c-0xffffffffffff"
thread: 1 nab: 0xffffffff seq: 0x0000000f hws: 0x2 eot: 1 dis: 0
resetlogs count: 0x29266e12 scn: 0x0000.0008187f (530559)
resetlogs terminal rcv count: 0x0 scn: 0x0000.00000000
prev resetlogs count: 0x23696b6f scn: 0x0000.00000001 (1)
prev resetlogs terminal rcv count: 0x0 scn: 0x0000.00000000
Low scn: 0x0000.000f223c (991804) 07/13/2009 18:57:57
Next scn: 0xffff.ffffffff 01/01/1988 00:00:00
Enabled scn: 0x0000.0008187f (530559) 06/24/2009 13:26:42
Thread closed scn: 0x0000.000f223c (991804) 07/13/2009 18:57:57
我们看到,这里的low scn就是first_change。还可以看到next scn是个无穷大的数字。
这时候,我们通过命令执行一次checkpoint:
SQL> alter system switch logfile;
System altered.
SQL> select * from v$log;
GROUP# THREAD# SEQUENCE# BYTES MEMBERS ARC STATUS
---------- ---------- ---------- ---------- ---------- --- ----------------
FIRST_CHANGE# FIRST_TIME
------------- ------------
1 1 16 52428800 1 NO CURRENT
996605 13-JUL-09
2 1 15 52428800 1 YES INACTIVE
991804 13-JUL-09
3 1 14 52428800 1 YES INACTIVE
966433 12-JUL-09
可以看到,这时候日志文件2状态时inactive,数据库在使用日志文件1。日志文件1的first_change应该与控制文件的系统scn,数据文件scn和数据文件文件头的scn相等,我们验证一下:
1* select checkpoint_change# from v$datafile
SQL> /
CHECKPOINT_CHANGE#
------------------
996605
996605
996605
996605
996605
996605
6 rows selected.
1* select checkpoint_change# from v$database
SQL> /
CHECKPOINT_CHANGE#
------------------
996605
1 select checkpoint_change#
2* from v$datafile_header
SQL> /
CHECKPOINT_CHANGE#
------------------
996605
996605
996605
996605
996605
996605
6 rows selected.
可见,在执行过checkpoint之后,next scn更新了控制文件的系统scn,数据文件scn和数据文件文件头的启动scn。
如果数据正常关闭的话,在关闭之前会做一个checkpoint操作,将log file中的数据写入数据文件中,将控制文件,数据文件的scn更新为最新的scn。
当数据库启动时,oracle会检查控制文件中每个datafile checkpoint scn和数据文件文件头的启动scn是否相等,再检查每个datafile checkpoint scn和stop scn是否相等。
如发现不同,则从redo log中找到丢失的scn,重新写入数据文件中进行恢复。
几个检查点SCN号的关系
1) 几个检查点SCN都相同:在数据库打开并运行之后,控制文件中的系统检查点SCN、控制文件中的数据文件检查点SCN及每个数据文件头中的启动SCN都是相同的,控制文件中的每个数据文件的终止SCN都是NULL。
2) 数据库正常关闭时,系统会执行一个检查点动作,这时所有数据文件的 终止SCN号会设置为数据文件头的那个启动SCN。数据库重新启动时,Oracle将数据文件头中的启动SCN与数据文件检查点SCN比较,如果这两个值匹配,Oracle接下来再比较数据文件头中的SCN和控制文件中数据文件的终止SCN,如果这个值也匹配,就意味着所有数据块已经提交,因此数据库不需要进行恢复,此时数据库直接打开。当所有的数据文件都打开之后,终止SCN再次被设置为NULL,表示数据文件已经打开并能够正常使用了。
3) 数据库非正常关闭(或称为实例崩溃)时,终止SCN不会被设置,依然为NULL,这可以通过把数据库启动至mount状态查询出来。这样Oracle通过这个信息就可以知道实例上次运行时崩溃了,检查点没有执行。这样重新启动时,Oracle会执行实例恢复工作,即先执行前滚、回滚操作,再把数据库打开。
4) 数据文件检查点SCN及系统检查点SCN比文件头启动SCN大:这时的情况是:系统发生介质故障,数据文件被以前的备份代替,控制文件中的数据文件检查点SCN肯定比文件头中的启动SCN要大,这样Oracle就知道要对这个文件进行介质恢复。这时要通过下面语句恢复数据库:
recover database ……
5) 系统检查点SCN比数据文件SCN及文件头启动SCN大:
有些表空间是只读的,这时控制文件中的系统检查点SCN号会不断增长,而数据文件SCN号和文件头中的启动SCN号会停止更新(直到表空间又设置为可读写),显然这时系统检查点SCN号会大于数据文件SCN和文件头启动SCN。
6) 系统检查点SCN及数据文件SCN比文件头启动SCN小:
在数据库恢复时,控制文件可能不是最新的,即把一个较早的控制文件还原为当前的控制文件,然后再执行恢复操作,这时控制文件中的系统检查点SCN和数据文件SCN可能比文件头的启动SCN小。这时恢复数据库要用下面命令:
recover database using Backup Controlfile或其他的恢复语句。