• Oracle 系统改变号SCN详解


    http://czmmiao.iteye.com/blog/1010267

    Scn : scn的英文全称就是 system change number, 中文直译过来就是系统改变号了.

    1. 1.   SCN的定义

    scn的英文全称就是 system change number, 中文直译过来就是系统改变号了.

    有名字可以知道, scn其实是一串数字. 那么它到底是用来做什么的.

    其实scn是oracle 根据1个时间点, 然后经过1个函数的运算后得出的1个数字. (x和y一一对应), 反之, 也可以通过scn号码经过反函数的运算得出时间.

    也就是说, scn其实就是时间的另1种表示.

    2. 系统为什么要把时间转化为SCN

    原因很简单, 因为系统要用不同的SCN号码来比较, 例如A对象的SCN号码和B对象的SCN号码. 系统就会知道哪个SCN的号码更新, 就知道SCN对应的时间更迟了.

     

    也就是说, oracle 会利用scn 来比较时间上的先后或者新旧.

     

    那为什么不用直接用时间来比较呢? 因为直接比较时间的话比较复杂啊.

     

    回想起, sharepool 解析sql的概念, oracle会判断sql语句 在sharepool 里有没有被执行(解析)过, 如何判断呢? 很简单, 就是根据要执行的sql语句 跟sharepool 缓存的sql语句(其实是sql]语句的哈希码)比较啊.

    问题是就是怎样比较了, 就是把sql语句的ASCII码根据函数转化为1个哈希码(数字)啊. 而不是直接地比较两条sql语句啊.

     

    根本原因就是计算内部比较数字是很简单的, 而比较复杂一点的数据类型, 例如字符串,或者时间, 就相对复杂了.

    所以系统会把时间转化为SCN, 就是为了方便不同时间之间的比较啊.

     

    可以利用下面语句来获得当前的时间及当前时间所对应的scn:

    1. SQL> select dbms_flashback.get_system_change_number,SCN_TO_TIMESTAMP(dbms_flashback.get_system_change_number) from dual;
    2.  
    3. GET_SYSTEM_CHANGE_NUMBER SCN_TO_TIMESTAMP(DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER)
    4. ------------------------ ---------------------------------------------------------------------------
    5. 1760283 29-APR-13 11.25.40.000000000 PM


    那个1760283 就是SCN啦.

    3. SCN的意义

    SCN的意义很简单, 就是为了保证oracle数据和文件的一致性, 下面会详细分析常见的SCN, 来体会这个意义.

     

    4. 常见的SCN

    4.1 关于checkpoint queueSCN

    我们先回顾下检查点队列的经典SQL 查询语句, 如下:

    [plain] view plaincopy

    1. SQL> select CPDRT,CPLRBA_SEQ||'.'||CPLRBA_BNO||'.'||CPLRBA_BOF "Low RBA",CPODR_SEQ||'.'||CPODR_BNO||'.'||CPODR_BOF "On disk RBA",CPODS,CPODT,CPHBT from x$kcccp;
    2.  
    3. CPDRT Low RBA On disk RBA CPODS CPODT CPHBT
    4. ---------- -------------------- -------------------- ---------------- -------------------- ----------
    5. 159 51.52589.0 51.53508.0 1762856 04/29/2013 23:36:38 810512439
    6. 0 0.0.0 0.0.0 0 0
    7. 0 0.0.0 0.0.0 0 0
    8. 0 0.0.0 0.0.0 0 0
    9. 0 0.0.0 0.0.0 0 0

    10. 0 0.0.0 0.0.0 0 0

    11. 0 0.0.0 0.0.0 0 0

    12. 0 0.0.0 0.0.0 0 0

    1. 13.  

    14. 8 rows selected.

     

    上次讲过了, 上面CPDRT就是当前脏buffer的个数, Low_RBA就是checkpoint queue中最高记录的日志地址, 也是整个系统最早脏buffer日志地址, 而on disk RBA就是current logfile(当前使用的日志文件)最后一条日志的地址, 也就是硬盘上最后1条日志地址. oracle每3秒发生1此检查点时间, 会把当前的Low RBA保存到控制文件中. 假如oracle在某个时间点不幸崩溃了, 那么重启恢复时, 就会在控制文件中找出最新保存的Low RBA作为起点, on disk RBA作为终点, 把这段日志对应的脏buffer恢复出来.

     

    好了, 接着 CPODS 这1列就是1个SCN了, 但是它不是Low RBA被写入时的SCN, 而是ondisk RBA被写入的时间对应的SCN啊. 这个SCN对于oracle来讲其实是很有用的.

     

    4.3 控制文件的SCN介绍

    控制文件包括3种重要的SCN, 包括系统检查点SCN, 保存的数据文件的检查点SCN和数据文件的结束SCN.

    没错, 后面的关于数据文件的SCN其实是数据文件的, 但是会被保存在控制文件内. 作用就是为了校验数据文件的一致性和系统的状态. 具体的本文后面还会讲到.

     

    至于控制文件本身的检查点的SCN又叫系统检查点SCN. 该SCN是全局范围的, 当检查点进程ckpt启动时, oracle 就把系统检查点SCN保存到控制文件中. 当发生文件级别的SCN时, 例如将某个表空间置于只读状态, 不会更新检查点SCN.

     

    可以用下面的语句来查看系统检查点SCN

    [sql] view plaincopy

    1. SQL> select CHECKPOINT_CHANGE# from v$database;
    2.  
    3. CHECKPOINT_CHANGE#
    4. ------------------
    5. 1794918

     

     

     

    4.3 数据文件的SCN介绍

    Oracle会为数据文件保存2个SCN, 分别是数据文件的检查点SCN, 和结束SCN.

    4.3.1 数据文件的检查点SCN

    当ckpt进程启动时, 包括全局范围的改动(例如日志切换) 和文件级别的检查点(将标空间置为只读), 就会更新数据文件的SCN, 并将这SCN写于到控制文件中.

     

    可以用如下语句来查看数据库各个数据文件的SCN.

    [sql] view plaincopy

    1. SQL> select file#, name, checkpoint_change# from v$datafile;
    2.  
    3. FILE# NAME CHECKPOINT_CHANGE#
    4. ---------- ------------------------------------------------------------ ------------------
    5. 1 /u01/app/oracle/project/oradata/orcl/system01.dbf 1794918
    6. 2 /u01/app/oracle/project/oradata/orcl/sysaux01.dbf 1794918
    7. 3 /u01/app/oracle/project/oradata/orcl/undotbs01.dbf 1794918
    8. 4 /u01/app/oracle/project/oradata/orcl/users01.dbf 1794918
    9. 5 /u01/app/oracle/project/oradata/orcl/ts_example.dbf 1794918

     

    见到各个数据文件的SCN与 当前上面的系统检查点SCN是一致的.

    假如我将1个表空间设为只读, 则个这个表空间的数据文件的检查点SCN会被更新:

    [sql] view plaincopy

    1. SQL> alter tablespace ts_example read only;
    2.  
    3. Tablespace altered.
    4.  
    5. SQL> select file#, name, checkpoint_change# from v$datafile;
    6.  
    7. FILE# NAME CHECKPOINT_CHANGE#
    8. ---------- ------------------------------------------------------------ ------------------
    9. 1 /u01/app/oracle/project/oradata/orcl/system01.dbf 1794918

    10. 2 /u01/app/oracle/project/oradata/orcl/sysaux01.dbf 1794918

    11. 3 /u01/app/oracle/project/oradata/orcl/undotbs01.dbf 1794918

    12. 4 /u01/app/oracle/project/oradata/orcl/users01.dbf 1794918

    13. 5 /u01/app/oracle/project/oradata/orcl/ts_example.dbf 1801087


    如上面的例子, tx_exmple.dbf 这个数据文件的检查点scn被更新了.

    这时我们再查看系统检查点scn:

    [sql] view plaincopy

    1. SQL> select file#, name, checkpoint_change# from v$datafile;
    2.  
    3. FILE# NAME CHECKPOINT_CHANGE#
    4. ---------- ------------------------------------------------------------ ------------------
    5. 1 /u01/app/oracle/project/oradata/orcl/system01.dbf 1794918
    6. 2 /u01/app/oracle/project/oradata/orcl/sysaux01.dbf 1794918
    7. 3 /u01/app/oracle/project/oradata/orcl/undotbs01.dbf 1794918
    8. 4 /u01/app/oracle/project/oradata/orcl/users01.dbf 1794918
    9. 5 /u01/app/oracle/project/oradata/orcl/ts_example.dbf 1801087
    10. 10.  

    11. SQL> select checkpoint_change# from v$database;

    1. 12.  

    13. CHECKPOINT_CHANGE#

    14. ------------------

    15. 1794918


    见到系统检查点SCN并没有发生变化, 这时ts_example表空间的数据文件的检查点scn就比系统的检查点scn要新了.

     

    过了大概两小时, 再查一下..

    [plain] view plaincopy

    1. SQL> select file#, name, checkpoint_change# from v$datafile;
    2.  
    3. FILE# NAME CHECKPOINT_CHANGE#
    4. ---------- ------------------------------------------------------------ ------------------
    5. 1 /u01/app/oracle/project/oradata/orcl/system01.dbf 1815478
    6. 2 /u01/app/oracle/project/oradata/orcl/sysaux01.dbf 1815478
    7. 3 /u01/app/oracle/project/oradata/orcl/undotbs01.dbf 1815478
    8. 4 /u01/app/oracle/project/oradata/orcl/users01.dbf 1815478
    9. 5 /u01/app/oracle/project/oradata/orcl/ts_example.dbf 1801087


    发现1 ~ 4号的数据文件的检查点scn和系统检查点scn都更新了了, 但是5号文件却没更新, 于是变得比系统检查点scn旧了.

    至于系统检查点scn何时为何被更新, 下面会讲.

     

    4.3.2 数据文件的结束SCN

    每个数据文件都有1个结束scn, 在数据的正常运行中, 只要数据文件在线且可以读写的, 则这个数据文件的结束scn为空(NULL), 否则就是1个具体的scn值, 代表这个数据文件什么时候被结束读写状态.

     

    用如下命令来查看表空间的状态, 就得出数据文件的状态, 还有数据文件的检查点scn和结束scn.

    [plain] view plaincopy

    1. SQL> select a.file#, a.name, t.status as TS_STATUS, a.checkpoint_change#, last_change#
    2. 2 from v$datafile a, dba_data_files b, dba_tablespaces t
    3. 3 where a.file# = b.file_id and b.tablespace_name = t.tablespace_name;
    4.  
    5. FILE# NAME TS_STATUS CHECKPOINT_CHANGE# LAST_CHANGE#
    6. ---------- ------------------------------------------------------------ --------- ------------------ ------------
    7. 1 /u01/app/oracle/project/oradata/orcl/system01.dbf ONLINE 1815478
    8. 2 /u01/app/oracle/project/oradata/orcl/sysaux01.dbf ONLINE 1815478
    9. 3 /u01/app/oracle/project/oradata/orcl/undotbs01.dbf ONLINE 1815478

    10. 4 /u01/app/oracle/project/oradata/orcl/users01.dbf ONLINE 1815478

    11. 5 /u01/app/oracle/project/oradata/orcl/ts_example.dbf READ ONLY 1801087 1801087

     

    可以见到, 1 ~ 4号文件的结束scn( last_change#) 是NULL, 而第5个文件是表空间ts_example的数据文件, 刚才被设为readonly 了, 所以这个文件相当于被关闭不能读写, 所以具有结束scn, 而且它的检查点scn不再与系统检查点同步.

     

    这时我们将表空间ts_example 恢复为读写状态, 就会令它的数据文件再次更新其检查点scn, 结束scn就变回null了.

    [sql] view plaincopy

    1. SQL> alter tablespace ts_example read write;
    2.  
    3. Tablespace altered.
    4.  
    5. SQL> select name, checkpoint_change#,last_change# from v$datafile;
    6.  
    7. NAME CHECKPOINT_CHANGE# LAST_CHANGE#
    8. ------------------------------------------------------------ ------------------ ------------
    9. /u01/app/oracle/project/oradata/orcl/system01.dbf 1815478

    10. /u01/app/oracle/project/oradata/orcl/sysaux01.dbf 1815478

    11. /u01/app/oracle/project/oradata/orcl/undotbs01.dbf 1815478

    12. /u01/app/oracle/project/oradata/orcl/users01.dbf 1815478

    13. /u01/app/oracle/project/oradata/orcl/ts_example.dbf 1822907

    1. 14.  

    15. SQL>

     

    4.3 日志文件的SCN介绍

    在redo log里每1条数据项都对应1个地址, 上次介绍checkpoint queue时讲过了. 而且每1条日志数据项都配1scn, 因为redo log file是严格按照日志的产生时间来记录日志的啊, 所以scn就是配备的时间信息了.

     

    而且每1个redo log文件在文件头都记录着两个scn, 它们是first scnnext scn, first scn就是日志文件第一条数据项的scn, 相对地next scn就是文件最后1条数据项的scn. 注意命名规则, first scn容易理解, 至于后面那个为什么不叫last scn而叫next scn? 因为它同时也是下一个日志组文件的first scn啊, 这样的话用于表示日志文件的衔接啊.

     

    同样可以理解出, 日志组文件的first scn 和 next scn就代表了日志组的范围, 说明了这个日志组记录的是哪1个时间段的日志啊!

     

    用下面语句可以查询这个两个scn:

    [plain] view plaincopy

    1. SQL> select * from v$log;
    2.  
    3. GROUP# THREAD# SEQUENCE# BYTES BLOCKSIZE MEMBERS ARC STATUS FIRST_CHANGE# FIRST_TIM NEXT_CHANGE# NEXT_TIME
    4. ---------- ---------- ---------- ---------- ---------- ---------- --- ---------------- ------------- --------- ------------ ---------
    5. 1 1 55 52428800 512 1 NO CURRENT 1837304 01-MAY-13 2.8147E+14
    6. 2 1 53 52428800 512 1 NO INACTIVE 1794918 30-APR-13 1815478 30-APR-13
    7. 3 1 54 52428800 512 1 NO INACTIVE 1815478 30-APR-13 1837304 01-MAY-13



    可以见到3号文件的next scn 就是1号文件的first scn, 2号文件的next scn 就是3号文件的first scn了, 只有第1个日志组文件的next scn是不可用的, 而且没有next time, 因为第1个日志组状态是 current啊.

     

    注意观察, 上面的日志组的first scn都在上面的语句的系统检查点scn出现过啊, 没错, 系统检查点的scn的变化跟日志组的first scn有密切关系, 下面会详细讲到.

     

     

    4.4 日志文件的SCN的意义

    这里顺便解析下日志组文件的3种状态:

    Current:

    就是指当前正在使用的日志文件组, 新产生的日志将会被写入这个日志组的文件.

    Active:

    非当前写入的文件, 但是对应这个文件内的日志的脏buffer还没完全写入dbf文件中, 也就说这个日志文件不能被覆盖重写, 因为一旦被覆盖重写, 万一对应的脏buffer丢失, 就没有日志把那些脏buffer重造出来了.

    Inactive:

    非当前写入的文件, 而且里面日志对应的脏buffer已经全部被dbwr写入到dbf文件中了(变成干净buffer), 所以这种日志文件能被覆盖重写. 所谓覆盖重写就是日志切换的意思啦. 当current的写满, 就会写下1个日志组, 当然前提是下1个日志组状态不能是Active啦.

     

    这时我们做1个操作, 就是手动切换日志文件2次:

    [sql] view plaincopy

    1. SQL> alter system switch logfile;
    2.  
    3. System altered.
    4.  
    5. SQL> alter system switch logfile;
    6.  
    7. System altered.
    8.  
    9. SQL> select * from v$log;
    10. 10.  

    11. GROUP# THREAD# SEQUENCE# BYTES BLOCKSIZE MEMBERS ARC STATUS FIRST_CHANGE# FIRST_TIM NEXT_CHANGE# NEXT_TIME

    12. ---------- ---------- ---------- ---------- ---------- ---------- --- ---------------- ------------- --------- ------------ ---------

    13. 1 1 58 52428800 512 1 NO ACTIVE 1842626 01-MAY-13 1844283 01-MAY-13

    14. 2 1 59 52428800 512 1 NO ACTIVE 1844283 01-MAY-13 1844286 01-MAY-13

    15. 3 1 60 52428800 512 1 NO CURRENT 1844286 01-MAY-13 2.8147E+14


    可以见到, 手动切换2次后, current 状态的日志组轮换到了第3组, 也就是第3组的原来日志被重写了. 而第1组和第组的状态是ACTIVE的, 证明还有对应的脏buffer 未被写入到dbf文件, 所以不能被覆盖和重写的.

    而且2号和3号日志的First SCN都被更新了, 这很合理啊, 因为2号文件和3号文件都被重写过了嘛, 毕竟刚才手动切换日志2次!

     

    这时我们查询一下系统检查点SCN 和 数据文件的SCN:

    [sql] view plaincopy

    1. SQL> select checkpoint_change# from v$database;
    2.  
    3. CHECKPOINT_CHANGE#
    4. ------------------
    5. 1842626
    6.  
    7. SQL> select file#, name, checkpoint_change# from v$datafile;
    8.  
    9. FILE# NAME CHECKPOINT_CHANGE#

    10. ---------- ------------------------------------------------------------ ------------------

    11. 1 /u01/app/oracle/project/oradata/orcl/system01.dbf 1842626

    12. 2 /u01/app/oracle/project/oradata/orcl/sysaux01.dbf 1842626

    13. 3 /u01/app/oracle/project/oradata/orcl/undotbs01.dbf 1842626

    14. 4 /u01/app/oracle/project/oradata/orcl/users01.dbf 1842626

    15. 5 /u01/app/oracle/project/oradata/orcl/ts_example.dbf 1842626

    1. 16.  

    17. SQL>

    可以见到系统检查点SCN 这个SCN值与上面第1组logfile 的SCN值是一样的.

    我们来分析下:

    假如这时数据库崩了, 那么重启时就会做实例恢复. 那么做实例恢复需要那些日志呢? 由上面的分析得知, Active状态的日志都是存在有脏buffer未写入dbf文件中的. 所以需要从最早的Active状态日志组起, 也就是上面的第一组日志, 然后一直到current状态的日志都要用于实例恢复啊.

     

    这时我们再手动把所有脏buffer写入dbf中.

    [sql] view plaincopy

    1. SQL> alter system flush buffer_cache;
    2.  
    3. System altered.
    4.  
    5. SQL> select * from v$log;
    6.  
    7. GROUP# THREAD# SEQUENCE# BYTES BLOCKSIZE MEMBERS ARC STATUS FIRST_CHANGE# FIRST_TIM NEXT_CHANGE# NEXT_TIME
    8. ---------- ---------- ---------- ---------- ---------- ---------- --- ---------------- ------------- --------- ------------ ---------
    9. 1 1 58 52428800 512 1 NO INACTIVE 1842626 01-MAY-13 1844283 01-MAY-13

    10. 2 1 59 52428800 512 1 NO INACTIVE 1844283 01-MAY-13 1844286 01-MAY-13

    11. 3 1 60 52428800 512 1 NO CURRENT 1844286 01-MAY-13 2.8147E+14

    1. 12.  

    13. SQL> select checkpoint_change# from v$database;

    1. 14.  

    15. CHECKPOINT_CHANGE#

    16. ------------------

    17. 1844286

    1. 18.  

    19. SQL> select file#, name, checkpoint_change# from v$datafile;

    1. 20.  

    21. FILE# NAME CHECKPOINT_CHANGE#

    22. ---------- ------------------------------------------------------------ ------------------

    23. 1 /u01/app/oracle/project/oradata/orcl/system01.dbf 1844286

    24. 2 /u01/app/oracle/project/oradata/orcl/sysaux01.dbf 1844286

    25. 3 /u01/app/oracle/project/oradata/orcl/undotbs01.dbf 1844286

    26. 4 /u01/app/oracle/project/oradata/orcl/users01.dbf 1844286

    27. 5 /u01/app/oracle/project/oradata/orcl/ts_example.dbf 1844286


    见到写入所有脏buffer后, 第1和第2个日志组文件.状态就变成INACTIVE了, 也就是可以被重写啦!

    如果这时数据库崩溃, 所需的日志文件就只是当前current 日志了, 因为INACTIVE的日志所对应的脏buffer已经全部写入dbf文件!

    而且数据文件的检查点SCN和系统检查点SCN都被更新了, 因为刚才的动作相当执行了1次完全检查点事件, 所有数据文件都会被更新, 所以数据文件的检查点SCN也会更新. 当然,系统检查点SCN也更新了. 而且更新的后的值与Current状态的日志文件的First SCN 是一致的.

     

    所以日志文件的SCN也是用来标识日志的新旧程度的, 而且会把最早不能被重写的日志组first SCN作为系统检查点SCN写入控制文件中.

     

    总结: 可以见到, 系统检查点的SCN实际上就是最早1Active状态的日志组的first SCN, 或者是current状态的日志组SCN(当没有ACTIVE状态的日志组时), 当数据库崩溃时, oracle重启后会做实例恢复, 就会从控制文件得出最新的系统检查点SCN, 然后根据这个SCN获得第1个需要被回复的日志啊, 当然,具体从那个日志的哪个数据项开始还要看被保存于控制文件的检查点队列的LRBA.

     

    4.5 控制文件的和数据文件SCN的意义

    我们上面提到了,控制文件的系统检查点SCN是如何生成的了, 但是再上面还提到了其实控制文件还保存着数据文件的检查点SCN和结束SCN, 这里会详细分析它们的关系及意义.

     

    首先, 上面说过, 结束SCN是当文件结束在线状态或结束读写状态时, 就会生成结束SCN, 那么正常情况下, 除了手动更改数据文件的状态, 只有关机这1个动作, 会生成数据文件的结束SCN(last_change#)

     

    而且关机时, oracle还会做1个动作, 就是把每个dbf文件生成的结束SCN保存入控制文件中, 那么下次启动oracle数据库时,oracle会检查控制文件, 当然发现正常的dbf结束SCN,就认为自己上次关闭是正常关闭.

     

    假如, 数据库在运行中崩了, 那么oracle就不会生成dbf文件结束scn和把它们写入控制文件, 下次oracle启动自检时发现控制文件不存在数据文件的结束scn, 那么oracle就会认为上次关闭是非正常关闭, 接下来就启动实例恢复了. 所以保存在控制文件的数据文件结束SCNoracle启动自检时判断上次关闭是否正常关闭的标记.

     

     

    还有, 正常关闭下所有数据文件的检查点SCN与系统检查点SCN是保持一致的, 假如这时我们做1个动作, 就是在数据库关闭状态时, 用较旧版本的1个数据文件去覆盖正常的数据文件, 那么启动时oracle发现这个数据库文件的scn比 控制文件中保存的scn旧, 就会认为介质故障, 就会要求启动介质恢复(需要归档日志), 这时通过日志会旧的dbf文件跑成新的. 所以行业内有一句话: 提升数据库文件的SCN!

     

    当oracle自检时发现控制文件保存的系统检查点SCN比数据库文件的检查scn还旧, 就会认为控制文件发生故障..这时恢复数据库要用下面命令:recover database using Backup Controlfile或其他的恢复语句。

     

     

    4.5 1个关键的参数 fast_start_mttr_target

    看这个参数的名字, 大概估算出这个参数跟启动速度有关.

    没错, 这个参数参数就是用来设定数据库崩溃后, 实例恢复的期望速度. 这个参数的单位是秒, 假如我们把这个参数设为20秒, 那么oracle就会调整一些参数, 让实例回复的速度控制在20秒以内.

     

    关键是调整什么啊, 我们知道实例恢复的本质是构造出丢失的脏buffer, 所以崩溃时脏buffer的数量直接影响着实例恢复的速度. 所以oracle就会调整脏buffer的数量, 怎样调整? 就是调整dbwr的写入频率啊.

  • 相关阅读:
    游LeetCode一月之闲谈
    新年计划与企盼
    F#周报2019年第51&52期
    F#周报2019年第50期
    F#周报2019年第49期
    F#周报2019年第48期
    F#周报2019年第47期
    F#周报2019年第46期
    F#周报2019年第45期
    关于我的随笔
  • 原文地址:https://www.cnblogs.com/weixun/p/3278637.html
Copyright © 2020-2023  润新知