zoukankan      html  css  js  c++  java
  • oracle scn的研究转自听海日志

         今天在看盖大师的深入解析oracle一书看到关于scn的东西,不是十分理解于是在pub上看到一篇关于scn的研究,写的不错。于是转载下来,供以后复习。

    地址:http://www.itpub.net/thread-1601493-1-1.html

    内容:

    一、SCN基础
    1、什么是SCN

    Concepts中是这样描述SCN的:

    A system change number (SCN) is alogical, internal timestamp used by Oracle Database. SCNs order events thatoccur within the database, which is necessary to satisfy the ACID properties ofa transaction. Oracle Database uses SCNs to mark the SCN before which allchanges are known to be on disk so that recovery avoids applying unnecessaryredo. The database also uses SCNs to mark the point at which no redo exists fora set of data so that recovery can stop.

    怎么理解这个“SCN(系统变更号)是供Oracle数据库使用的一个逻辑的、内部的时间戳”呢?要理解这个先需要理解Oracle中的事务(Transaction)和数据一致性(Data Consistency)的概念。

    先说说数据一致性的概念。数据一致性指的是数据的可用性。比如说管理一个财务的系统,需要从A账户将100元转入到B账户,正常的操作是从A账户减去100元,然后给B账户加上100元,如果这两步操作都正常完成了,那我们可以说完成转账操作之后的数据是一致可用的;但是如果在操作的过程中出了问题,A账户的100元给减掉了,但是B账户却没有加上100元,这样的情况下产生的结果数据就有问题了,因为部分操作的失败导致了数据的不一致而不可用,在实际中肯定是要避免这种让数据不一致的情况发生的。在Oracle数据库中,保证数据一致性的方法就是事务。事务是一个逻辑的、原子性的作业单元,通常由一个或者是多个SQL组成,一个事务里面的所有SQL操作要么全部失败回滚(Rollback),要么就是全部成功提交(Commit)。就像上面转账的例子,为保证数据的一致性,就需要将转账的两步操作放在一个事务里面,这样不管哪个操作失败了,都需要将所有已进行的操作回滚,以保证数据的可用性。进行事务管理是数据库区别于别的文件系统的一个最主要的特征,在数据库中事务最主要的作用就是保证了数据的一致性,每次事务的提交都是将数据库从一种一致性的状态带入到另外一种一致性的状态中,SCN就是用来对数据库的每个一致状态进行标记的,每当数据库进入到一个新的一致的状态,SCN就会加1,也就是每个提交操作之后,SCN都会增加。也许你会想为什么不直接记录事务提交时候的时间戳呢?这里面主要是涉及了两个问题,一个是时间戳记录的精度有限,再一个就是在分布式系统中记录时间戳会存在系统时钟同步的问题,详细的讨论可以查看Ordering Events in Oracle。

    SCN在数据库中是一个单一的不断的随着数据库一致性状态的改变而自增的序列。正如一个时间戳代表着时间里面的某一个固定的时刻点一样,每一个SCN值也代表着数据库在运行当中的一个一致性的点,大的SCN值所对应的事务总是比小SCN值的事务发生的更晚。因此把SCN说成是Oracle数据库的逻辑时间戳是很恰当的。
    2、SCN概述

    SCN是当Oracle数据库更新后,由DBMS去自动维护累积递增的一个数字。通常看文章的时候能看到各种类型的SCN,但是严格来说SCN是没有分类的,之所以会有不同类型的SCN并不是说这些SCN的概念不一样,而是说不同分类的SCN代表的意义不一样,不管什么时候SCN所指代的都是数据库的某个一致性的状态。就像我们给一天中的某个时间点定义上班时间、另外的某个时间点定义成下班时间一样,数据库Checkpoint发生点的SCN被称为Checkpoint SCN,仅此而已。Oracle数据库中一共有4种SCN分别为:

    •系统检查点SCN (System Checkpoint SCN)

    •文件检查点SCN (Datafile Checkpoint SCN)

    •结束SCN (Stop SCN)

    •数据文件头SCN (Start SCN)

    其中前面3中SCN存在于控制文件中,最后一种则存在于数据文件的文件头中,在Oracle中用来标识数据库的每一次改动,及其先后顺序,SCN的最大值是0xffff.ffffffff。在控制文件中,SystemCheckpoint SCN是针对整个数据库全局的,因而只存在一个,而Datafile Checkpoint SCN和Stop SCN是针对每个数据文件的,因而一个数据文件就对应在控制文件中存在一份Datafile Checkpoint SCN和Stop SCN。在数据库正常运行期间,Stop SCN(通过视图v$datafile的字段last_change#可以查询)是一个无穷大的数字或者NULL。
    1、系统检查点SCN

    系统检查点SCN位于控制文件中,当检查点进程启动时(ckpt),Oracle就把系统检查点的SCN存储到控制文件中。该SCN是全局范围的,当发生文件级别的SCN时,例如将表空间置于只读状态,则不会更新系统检查点SCN。查询系统检查点SCN的命令如下:

    sys@ORCL>select CHECKPOINT_CHANGE# from v$database;

    CHECKPOINT_CHANGE#

    ------------------

            21655892
    2、文件SCN

    当ckpt进程启动时,包括全局范围的(比如日志切换)以及文件级别的检查点(将表空间置为只读、begin backup或将某个数据文件设置为offline等),这时会在控制文件中记录的SCN。查询数据文件SCN的命令如下:

    sys@ORCL> altertablespace users read only;

    Tablespace altered.

    sys@ORCL>select file#,checkpoint_change# from v$datafile;

         FILE# CHECKPOINT_CHANGE#

    ---------- ------------------

           1        21655892

           2        21655892

           3        21655892

           4        21657577

           5        21657577

           6        21655892

     

    6 rows selected.

    sys@ORCL>select checkpoint_change# from v$database;

    CHECKPOINT_CHANGE#

    ------------------

            21655892

    可以看到4、5号文件也就是users表空间所属的文件SCN值和其他文件不一致,且比系统检查点的SCN要大(因为这两个数据文件最后被操作,其他数据文件没有变动)。


    3、结束SCN

    每个数据文件都有一个结束SCN,在数据库的正常运行中,只要数据文件在线且是可读写的,结束SCN为NULL。否则则存在具体的SCN值。结束SCN也记录在控制文件中。

    sys@ORCL>select TABLESPACE_NAME,STATUS from dba_tablespaces;

    TABLESPACE_NAME              STATUS

    ----------------------------------------------

    SYSTEM                         ONLINE

    SYSAUX                         ONLINE

    UNDOTBS1                    ONLINE

    TEMP                        ONLINE

    USERS                           READ ONLY

    sys@ORCL>select file#,LAST_CHANGE# from v$datafile;         

         FILE# LAST_CHANGE#

    ---------- ------------

           1

           2

           3

           4     21657577

           5     21657577

           6

     

    6 rows selected.

    可以看到除了users表空间的结束SCN不为空,其他数据文件的结束SCN为空。将数据库至于mount状态,由于该状态下所有的数据文件都不可写,故mount状态下所有的数据文件都具有结束SCN。

    sys@ORCL>shutdown immediate

    Database closed.

    Database dismounted.

    ORACLE instance shut down.

    sys@ORCL>startup mount

    ORACLE instance started.

     

    Total System Global Area  263049216 bytes

    Fixed Size              2212448 bytes

    Variable Size              230690208 bytes

    Database Buffers        25165824 bytes

    Redo Buffers               4980736 bytes

    Database mounted.

    sys@ORCL>select file#,last_change# from v$datafile;

     

         FILE# LAST_CHANGE#

    ---------- ------------

           1    21657939

           2    21657939

           3    21657939

           4    21657577

           5    21657577

           6    21657939

     

    6 rows selected.


    4、数据文件头SCN

    不同于上述的SCN数据文件开始SCN记录在每个数据文件中。当发生系统及文件级别的检查点后,不仅将这时的SCN号记录在控制文件中,同样也记录在数据文件中。查询数据文件头SCN的命令如下:

    sys@ORCL>select file#,CHECKPOINT_CHANGE# from v$datafile_header;

     

    FILE# CHECKPOINT_CHANGE#

    ---------- ------------------

           1        21657939

           2        21657939

           3        21657939

           4        21657577

           5        21657577

           6        21657939

     

    6 rows selected.


    3、SCN相关概念
    1、Redo log中的high SCN和low SCN

    Oracle的Redo log会顺序纪录数据库的各个变化。一组redo log文件写满后,会自动切换到下一组redo log文件。则上一组redo log的high SCN就是下一组redo log的low SCN。在current log中high SCN为无穷大。可通过查询v$log_history查看 low SCN和 high SCN:

    sys@ORCL>select recid,sequence#,first_change#,next_change# from v$log_history;

         RECID SEQUENCE# FIRST_CHANGE#     NEXT_CHANGE#

    ---------- ---------------------- --------------------

           1      2      21,069,995       21,085,568

           2      1      21,085,569       21,105,418

           3      2      21,105,418       21,132,018

           4      3      21,132,018       21,163,953

           5      4      21,163,953       21,191,900

           6      5      21,191,900       21,209,347

           7      6      21,209,347       21,223,812

           8      7      21,223,812        21,232,963

           9      8      21,232,963       21,263,223

          10      9      21,263,223        21,288,586

          11     10      21,288,586        21,301,971

          12      1      21,300,115        21,324,025

          13      2      21,324,025        21,344,131

          14      3      21,344,131        21,347,920

          15      4      21,347,920        21,369,784

          16      5      21,369,784        21,374,509

          17      6      21,374,509        21,374,914

          18      7      21,374,914        21,375,336

          19      8      21,375,336        21,384,136

          20      9      21,384,136        21,409,089

          21     10      21,409,089        21,446,217

          22     11      21,446,217        21,496,982

          23     12      21,496,982        21,509,283

          24     13      21,509,283        21,545,787

          25     14      21,545,787        21,564,418

          26     15      21,564,418        21,581,068

          27     16      21,581,068        21,582,611

          28     17      21,582,611        21,582,860

          29     18      21,582,860        21,583,942

          30     19      21,583,942        21,609,862

          31     20      21,609,862        21,630,988

          32     21      21,630,988        21,655,891

     

    32 rows selected.


    2、查看currnet redolog中的high SCN

    sys@ORCL>select vf.member,v.status,v.first_change# from v$logfile vf,v$log v wherevf.group#=v.group# and v.status='CURRENT';

    MEMBER                                 STATUS         FIRST_CHANGE#

    ------------------------------------------------------------------------------------------------------------------

    /opt/oracle/oradata/orcl/redo01.log              CURRENT        21,655,891

    sys@ORCL> altersystem dump logfile '/opt/oracle/oradata/orcl/redo01.log';

    System altered.

    sys@ORCL> showparameter user_dump

    NAME                       TYPE  VALUE

    ----------------------------------------------- ------------------------------

    user_dump_dest         string /opt/oracle/diag/rdbms/orcl/orcl/trace

    打开转储出来的文件,可以看到:

    DUMP OF REDO FROM FILE'/opt/oracle/oradata/orcl/redo01.log'

    Opcodes *.*

    RBAs: 0x000000.00000000.0000 thru0xffffffff.ffffffff.ffff

    SCNs: scn: 0x0000.00000000 thru scn:0xffff.ffffffff

    Times: creation thru eternity

    FILE HEADER:

          CompatibilityVsn = 186646528=0xb200000

          DbID=1284637334=0x4c920296, Db Name='ORCL'

          ActivationID=1296530476=0x4d477c2c

          ControlSeq=19392=0x4bc0, File size=102400=0x19000

          FileNumber=1, Blksiz=512, File Type=2 LOG

    descrip:"Thread 0001, Seq# 0000000022,SCN 0x0000014a7153-0xffffffffffff"

    thread: 1 nab: 0xffffffff seq: 0x00000016 hws:0x5 eot: 1 dis: 0

    resetlogs count: 0x2dd248e0 scn:0x0000.01450393 (21300115)

    prev resetlogs count: 0x2dce6ef7 scn:0x0000.0141bd81 (21085569)

    Low scn: 0x0000.014a7153 (21655891)03/20/2012 09:18:56

    Next scn: 0xffff.ffffffff 01/01/1988 00:00:00

    Enabled scn: 0x0000.01450393 (21300115)12/01/2011 15:18:56

    Thread closed scn: 0x0000.014a7953 (21657939)03/20/2012 09:43:56

    Disk cksum: 0x202e Calc cksum: 0x202e

    Terminal recovery stop scn: 0x0000.00000000

    Terminal recovery  01/01/1988 00:00:00

    Most recent redo scn: 0x0000.00000000

    Largest LWN: 0 blocks

    End-of-redo stream : No

    Unprotected mode

    Miscellaneous flags: 0x800000

    Thread internal enable indicator: thr: 0, seq:0 scn: 0x0000.00000000

    redo log中当前系统的SCN记录当前最新的数据库SCN值可通过如下命令查看:

    sys@ORCL>select dbms_flashback.get_system_change_number from dual;

    GET_SYSTEM_CHANGE_NUMBER

    ------------------------

                 21658581

    如果需要进行实例恢复,则需恢复的记录为21655891至21658581中redo log中的记录。


    3、日志切换或者checkpoint

    当日志切换或发生checkpoint时,从Low SCN到Next SCN之间的所有redo记录的数据就被DBWn进程写入数据文件中,而CKPT进程则将所有数据文件(无论redo log中的数据是否影响到该数据文件)的文件头上记录的Start SCN(通过视图v$datafile_header的字段checkpoint_change#可以查询)更新为Next SCN,同时将控制文件中的System Checkpoint SCN(通过视图v$database的字段checkpoint_change#可以查询)、每个数据文件对应的DatafileCheckpoint(通过视图v$datafile的字段checkpoint_change#可以查询)也更新为Next SCN。但是,如果该数据文件所在的表空间被设置为read-only时,数据文件的Start SCN和控制文件中DatafileCheckpoint SCN都不会被更新。


    4、心跳

    在Oracle中有一个事件叫Heartbeat,这个词在很多地方被提及,并且有着不同的含义(比如RAC中),我们这里要讨论的是CKPT的Heartbeat机制。Oracle通过CKPT进程每3秒将Heartbeat写入控制文件,以减少故障时的恢复时间。


    5、数据库正常关闭启动

    数据库正常关闭时,系统会执行一个完全检查点动作,并用该检查点时的SCN号更新上述4个SCN号,这时所有数据文件的终止SCN号会设置为数据文件头的那个启动SCN(除了离线和只读的数据文件)。

    数据库重新启动时,Oracle将数据文件头中的启动SCN与数据文件检查点SCN比较,如果这两个值匹配,Oracle接下来再比较数据文件头中的SCN和控制文件中数据文件的终止SCN,如果这个值也匹配,就意味着所有数据块已经提交,因此数据库不需要进行恢复,此时数据库直接打开。当所有的数据文件都打开之后,在线且可读写的数据文件终止SCN再次被设置为NULL,表示数据文件已经打开并能够正常使用了。有些表空间是只读的,这时控制文件中的系统检查点SCN号会不断增长,而数据文件SCN号和文件头中的启动SCN(会停止更新直到表空间又设置为可读写),显然这时系统检查点SCN号会大于数据文件SCN和文件头启动SCN。


    6、数据库非正常关闭

    数据库非正常关闭(或称为实例崩溃)时,终止SCN不会被设置,依然为NULL,这可以通过把数据库启动至mount状态查询出来。这样重新启动时,SMON进程会执行实例恢复工作,即先执行前滚,再把数据库打开,最后执行回滚操作。


    7、数据文件介质故障

    出现介质故障时,数据文件检查点SCN及系统检查点SCN比文件头启动SCN大。系统发生介质故障时,数据文件被以前的备份代替,控制文件中的数据文件检查点SCN肯定比文件头中的启动SCN要大,这样Oracle就知道要对这个文件进行介质恢复。


    8、控制文件介质故障

    系统检查点SCN及数据文件SCN比数据文件头启动SCN小:

    在数据库恢复时,控制文件可能不是最新的,即把一个较早的控制文件还原为当前的控制文件,然后再执行恢复操作,这时控制文件中的系统检查点SCN和数据文件SCN可能比文件头的启动SCN小。这时恢复数据库要用下面命令:recover database using Backup Controlfile或其他的恢复语句。


    9、备份时的实例崩溃当执行begin backup时实例崩溃:控制文件中的数据文件检查点SCN号和数据文件头部检查点SCN号相同,但是每个可读写的在线数据文件之间检查点SCN号不同,那么要求介质恢复,例如发出beginbackup命令后就会出现这种情况,需要通过endbackup命令好才可以打开数据库。

     4、SCN查看与转换
    1、查看系统当前的SCN
    Oracle数据库提供了两种直接查看系统当前SCN的方法,一个是V$DATABASE中的CURRENT_SCN列,另外一个就是通过dbms_flashback.get_system_change_number得到。
    sys@ORCL> col SCN for 9999999999999
    sys@ORCL> SELECT current_SCN SCN FROM v$database;
               SCN
    --------------
          21660463
    sys@ORCL> SELECT DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER SCN FROM DUAL;
               SCN
    --------------
          21660479
    如果你好奇心足够的话也许会执行下面的语句,然后发现一个“惊讶”的结果:
    sys@ORCL> col SCN for 9999999999999
    sys@ORCL> col SCN2 for 9999999999999
    sys@ORCL> SELECT current_SCN SCN,DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER SCN2 FROM v$database;
               SCN                 SCN2
    -------------- --------------
          21660533             21660534
    不过请不要惊讶,这个很正常,毕竟语句的执行时需要时间的,虽然很短,不过还是能发生很多的事情。
    2、SCN与时间的相互转换
    一个SCN值总是发生在某一个特定的时刻的,只不过由于粒度的不一样,通常会存在多个SCN对应同一个时间戳。Oracle中提供了两个函数以供我们进行SCN和时间的互换:
    SCN_TO_TIMESTAMP(SCN_number) 将SCN转换成时间戳。
    TIMESTAMP_TO_SCN(timestamp) 将时间戳转换成SCN。
    下面就举几个例子来说明:
    将SCN转换成时间戳
    sys@ORCL> SELECT SCN_TO_TIMESTAMP(21660534) timestamp FROM DUAL;
    TIMESTAMP
    ---------------------------------------------------------------------------
    20-3月 -12 10.38.32.000000000 上午   -- 将时间戳转换成
    sys@ORCL> SELECT TIMESTAMP_TO_SCN(TO_TIMESTAMP('20-3月-12 10.38.32.000000000','DD-Mon-RR HH:MI:SS.FF A.M.')) SCN FROM DUAL;
               SCN
    --------------
          2 1660533
    很明显的能看到同样的时间戳,转换出来的SCN就是不一样,其根本原因就是粒度问题了。
    3、ORA_ROWSCN伪列
    从10g开始,Oracle提供了一个名为ORA_ROWSCN的伪列来让我们更近距离的接触SCN这个东西,利用这个伪列能很清楚的观察提交(Commit)操作对于表中SCN的影响。
    我们先来建立以下测试的数据表和数据(例子延伸自《Oracle9i & 10g编程艺术》一书),下面的例子进行两次,两次唯一的不同在于建表时参数不一样,我们通过这个来观察SCN记录的一些行为。
    试验一:
    建立表
    test@ORCL> CREATE TABLE t (x INT);
    Table created.
    插入数据
    test@ORCL> BEGIN FOR i IN 1 .. 5 LOOP
    INSERT INTO t VALUES(i );
    COMMIT;
    END LOOP;
    END;
    /
    查看结果
    SYS@ORCL> SELECT x, ORA_ROWSCN SCN,DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) BLOCKNO FROM t ORDER BY 2;
             X              SCN    BLOCKNO
    ---------- -------------- ----------
             1         21661219     146911
             2         21661219     146911
             5         21661219     146911
             4         21661219     146911
             3         21661219     146911
    试验二:
    建立表
    test@ORCL> CREATE TABLE t (x INT) ROWDEPENDENCIES;
    Table created.
    插入数据
    test@ORCL> BEGIN FOR i in 1 .. 5 LOOP
    INSERT INTO t VALUES (i);
    COMMIT;
    END LOOP;
    END;
    /
    查看结果
    test@ORCL> SELECT x, ORA_ROWSCN SCN, DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID) blockno FROM t ORDER BY 2;
             X              SCN    BLOCKNO
    ---------- -------------- ----------
             1         21661327     146919
             2         21661329     146919
             3         21661330     146919
             4         21661332     146919
             5         21661334     146919
    试验一中每行数据的SCN数据都是一样的,如果根据我们现在对SCN的理解的话每次提交之后SCN应该变化才对的,那问题出在哪里呢?问题就出在默认情况下Oracle数据库记录SCN是以数据库为单位记录的,因为在数据库读取数据的时候都是以数据块单位,而不是以行为单位的。
    要想达到按行记录SCN就必须在创建表的时候使用ROWDEPENDENCIES参数,ROWDEPENDENCIES用于打开一个数据表的行级的追踪,在行级维护SCN记录,使得我们能清楚的看到每次提交操作对于SCN的影响。试验二就是使用表的ROWDEPENDENCIES特性之后的结果。
    注:ROWDEPENDENCIES有个重要的作用是用来实现乐观锁定(Optimistic Locking),这个在《Oracle9i & 10g编程艺术》一书中有详细的描述。
    5、SCN的作用
    SCN描述的是数据一致性的状态,自然的它就会在各种涉及数据一致性的场合中起到重要作用的,下面列举的就是其中的一部分:
    •读数据的一致性
    •数据库恢复
    •Flashback
    •Stream
    •等等其他的
    说这个的原因就是想说SCN在Oracle中的应用无处不在,理解SCN是理解数据库许多其他功能工作原理的基础。
    在数据库启动过程中,当System Checkpoint SCN、Datafile Checkpoint SCN和Start SCN号都相同时,数据库可以正常启动,不需要做media recovery。三者当中有一个不同时,则需要做media recovery。
    如果在启动的过程中,End SCN号为NULL,则需要做instance recovery。这种情况一般是数据库不正常关闭导致。ORACLE在启动过程中首先检查是否需要media recovery,然后再检查是否需要instance recovery。
    二、SCN机制解析
    1、事务中的SCN
    SCN(System Chang Number)作为oracle中的一个重要机制,在数据恢复、Data Guard、Streams复制、RAC节点间的同步等各个功能中起着重要作用。理解SCN的运作机制,可以帮助你更加深入地了解上述功能。在理解SCN之前,我们先看下oracle事务中的数据变化是如何写入数据文件的:
    1、事务开始;
    2、在buffer cache中找到需要的数据块,否则,从数据文件中载入buffer cache中;
    3、事务修改buffer cache的数据块,该数据被标识为“脏数据”,并被写入log buffer中;
    4、事务提交,LGWR进程将log buffer中的“脏数据”写入redo log file中;
    5、当发生checkpoint,CKPT进程更新所有数据文件的文件头中的信息,DBWn进程则负责将Buffer Cache中的脏数据写入到数据文件中。
    经过上述5个步骤,事务中的数据变化最终被写入到数据文件中。但是,一旦在上述中间环节时,数据库意外宕机了,在重新启动时如何知道哪些数据已经写入数据文件、哪些没有写呢(同样,在DG、streams中也存在类似疑问:redo log中哪些是上一次同步已经复制过的数据、哪些没有)?SCN机制就能比较完善的解决上述问题。SCN是一个数字,确切的说是一个只会增加、不会减少的数字。正是它这种只会增加的特性确保了Oracle知道哪些应该被恢复、哪些应该被复制。
    在一个事务提交后(上述第四个步骤),会在redo log中存在一条redo记录,同时,系统为其提供一个最新的SCN(通过函数dbms_flashback.get_system_change_number可以知道当前的最新SCN),记录在该条记录中。如果该条记录是在redo log被清空(日志满做切换时或发生checkpoint时,所有变化日志已经被写入数据文件中),则其SCN被记录为redo log的low SCN。以后在日志再次被清空前写入的redo记录中SCN则成为Next SCN。
    当日志切换或发生checkpoint(上述第五个步骤)时,从Low SCN到Next SCN之间的所有redo记录的数据就被DBWn进程写入数据文件中,而CKPT进程则将所有数据文件(无论redo log中的数据是否影响到该数据文件)的文件头上记录的Start SCN(通过视图v$datafile_header的字段checkpoint_change#可以查询)更新为Next SCN,同时将控制文件中的System Checkpoint SCN(通过视图v$database的字段checkpoint_change#可以查询)、每个数据文件对应的Datafile Checkpoint(通过视图v$datafile的字段checkpoint_change#可以查询)也更新为Next SCN。但是,如果该数据文件所在的表空间被设置为read-only时,数据文件的Start SCN和控制文件中Datafile Checkpoint SCN都不会被更新。
    2、产生最新的SCN
    实际上,这个数字是由当时的timestamp转换过来的。每当需要产生一个最新的SCN到redo记录时,系统获取当时的timestamp,将其转换为数字作为SCN。我们可以通过函数SCN_TO_TIMESTAMP(10g以后)将其转换回timestamp:
    test@ORCL> select dbms_flashback.get_system_change_number get_scn, SCN_TO_TIMESTAMP(dbms_flashback.get_system_change_number) scn_to_timestamp from dual;
       GET_SCN SCN_TO_TIMESTAMP
    ---------- ---------------------------------------------------------------------------
      21662602 20-3月 -12 11.21.47.000000000 上午
    也可以用函数timestamp_to_scn将一个timestamp转换为SCN:
    test@ORCL> select timestamp_to_scn(SYSTIMESTAMP) as SCN from dual;
               SCN
    --------------
          21662618
    最后,SCN除了作为反映事务数据变化并保持同步外,它还起到系统的“心跳”作用——每隔3秒左右系统会刷新一次系统SCN。
    3、SCN在数据库恢复中的作用
    数据库在正常关闭(shutdown immediate/normal)时,会先做一次checkpoint,将log file中的数据写入数据文件中,将控制文件、数据文件中的SCN(包括控制文件中的Stop SCN)都更新为最新的SCN。
    数据库异常/意外关闭不会或者只更新部分Stop SCN。
    当数据库启动时,Oracle先检查控制文件中的每个Datafile Checkpoint SCN和数据文件中的Start SCN是否相同,再检查每个Datafile Checkpoint SCN和Stop SCN是否相同。如果发现有不同,就从Redo Log中找到丢失的SCN,重新写入数据文件中进行恢复。
    SCN作为Oracle中的一个重要机制,在多个重要功能中起着“控制器”的作用。了解SCN的产生和实现方式,帮助DBA理解和处理恢复、DG、Streams复制的问题。
    利用SCN机制,在Oracle10g、11g中又增加了一些很实用的功能——数据库闪回、数据库负载重现等。
    1、使用using backup controlfile
    1)使用备份控制文件。
    2)重建resetlogs控制文件,如果重建noresetlogs不必要使用using backup controlfile。
    2、alter database open resetlog
    指定RESETLOGS将重设当前LOG sequence number为1,抛弃所有日志信息。以下条件需要使用resetlog:
    1)在不完全恢复(介质恢复)。
    2)使用备份控制文件。
    使用resetlogs打开数据库后务必完整地备份一次数据库。
    3、create controlfile resetlogs/noresetlogs
    1)用Noresetlogs重建控制文件时,控制文件中datafile Checkpoint来自Online logs中的Current log头。
    2)用Resetlogs重建控制文件时,控制文件中datafile Checkpoint来自各数据文件头。
    当system SCN,datafile SCN,start SCN不全相等,需要介质恢复,如果stopSCN NULL需要实例恢复。resetlogs抛弃所有在上一次恢复没有用到的日志信息,确保不被重新用与恢复。
    4、各种异常情况需要的恢复
    系统正常关闭:
    system SCN=datafile SCN=start SCN=stop SCN
    1) system SCN=datafile SCN=start SCN,不需要介质恢复。
    2) stopSCN not NULL,不需要实例恢复。
    系统异常关闭:
    system SCN=datafile SCN=start SCN,stop SCN NULL
    1) system SCN=datafile SCN=start SCN,不需要介质恢复
    2) stopSCN NULL,需要实例恢复
    旧数据文件:
    system SCN=datafile SCN>start SCN,stop SCN NULL/notNULL
    1) system SCN=datafile SCN>start SCN,需要介质恢复成system SCN=datafile SCN=start SCN。
    2) stopSCN NULL,需要实例恢复,not NULL不需要实例恢复。
    备份控制文件:
    system SCN=datafile SCN<=start SCN(当数据文件为旧的相等),stop SCN notNULL/NULL
    1) system SCN=datafile SCN<=start SCN,需要使用using backup controlfile介质恢复成system SCN=datafile SCN=start SCN=current log SCN(当前日志最大SCN)
    2) 为保证上一次恢复没有用到log日志不被使用,必须resetlogs。
    重建noresetlogs控制文件:
    控制文件中 datafile Checkpoint来自Online logs中的Current log头。
    current log SCN=system SCN=datafile SCN>=start SCN,stop SCN not NULL/NULL
    1) current log SCN=system SCN=datafile SCN>=start SCN,需要介质恢复成system SCN=datafile SCN=start SCN=redolog SCN(当前日志最大SCN),stop SCN not NULL。
    2) stopSCN not NULL 不需要实例恢复。
    重建resetlogs控制文件:
    控制文件中datafile Checkpoint来自各数据文件头。
    system SCN>=datafile SCN=start SCN,stop SCN not NULL/NULL
    1) system SCN>=datafile SCN=start SCN,需要使用using backup controlfile介质恢复成system SCN=datafile SCN=start SCN(当前日志最大SCN),stop SCN not NULL。
    2) stop notNULL,因为SCN已经为redolog SCN,log已经不能使用,必须resetlogs。

    三、Oracle SCN研究
    1、SCN的管理方式
    Oracle对SCN的管理,分为单节点和RAC两种方式。
    1、单节点的instance中
    单节点的instance中,SCN值存在SGA区,由system commit number latch保护。任何进程要得到当前的SCN值,都要先得到这个latch。
    2、RAC/OPS环境中
    Oracle通过排队机制(Enqueue)实现SCN在各并行节点之间的顺序增长。具体有两种方法:
    Lamport算法:又称面包房算法,先来先服务算法。跟很多银行采用的排队机制一样。客户到了银行,先领取一个服务号。一旦某个窗口出现空闲,拥有最小服务号的客户就可以去空闲窗口办理业务。
    Commit广播算法:一有commit完成,最新的SCN就广播到所有节点中。
    上述两种算法可以通过调整初始化参数max_commit_propagation_delay来切换。在多数系统(除了Compaq Tur64 Unix)中,该参数的默认值都是700厘秒(centisecond),采用Lamport算法。如果该值小于100厘秒,Oracle就采用广播算法,并且记录在alert.log文件中。
    2、几种重要的SCN
    1、Commit SCN
    当用户提交commit命令后,系统将当前SCN赋给该transaction。这些信息都反映在redo buffer中,并马上更新到redo log文件里。
    2、Offline SCN
    除了System tablespace以外的任何表空间,当我们执行alter tablespace…offline normal;命令时,就会触发一个checkpoint,将内存中的dirty buffer写入磁盘文件中。Checkpoint完成后,数据文件头会更新checkpoint SCN和offline normal SCN值。其中数据库文件头的checkpoint SCN值可通过查询列x$kccfe.fecps得到。
    如果执行alter tablespace…offline命令时采用temporary或immediate选项,而不用normal选项时,offline normal SCN会被设成0。这样当数据库重启后通过resetlog方式打开时,该表空间就无法再改回在线状态。
    3、Checkpoint SCN
    当数据库内存的脏数据块(dirty blocks)写到各数据文件中时,就发生一次checkpoint。数据库的当前checkpoint SCN值存在x$kccdi.diSCN中。Checkpoint SCN在数据库恢复中起着至关重要的作用。无论你用何种办法恢复数据库,只有当各个数据库文件的checkpoint SCN都相同时,数据库才能打开。
    虽然参数“_allow_resetlogs_corruption”可以在checkpoint SCN不一致时强制打开数据库,但是这样的数据库在open后必须马上作全库的export,然后重建数据库并import数据。
    4、Resetlog SCN
    数据库不完全恢复时,在指定时间点后的SCN都无法再应用到数据库中。Resetlog时的SCN就被设成当前数据库SCN,redo log也会被重新设置。
    5、Stop SCN
    Stop SCN记录在数据文件头上。当数据库处在打开状态时,stop SCN被设成最大值0xffff.ffffffff。在数据库正常关闭过程中,stop SCN被设置成当前系统的最大SCN值。在数据库打开过程中,Oracle会比较各文件的stop SCN和checkpoint SCN,如果值不一致,表明数据库先前没有正常关闭,需要做恢复。
    6、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。
    test@ORCL> col recid format 9999
    test@ORCL> col requence# format 9999
    test@ORCL> col first_change# format 9,999,999,999,999
    test@ORCL> col next_change# format 9,999,999,999,999
    test@ORCL> select recid,sequence#,first_change#,next_change# from v$log_history where rownum<6;
    RECID SEQUENCE#      FIRST_CHANGE#         NEXT_CHANGE#
    ----- --------- ------------------ ------------------
        1              2         21,069,995           21,085,568
        2              1         21,085,569           21,105,418
        3              2         21,105,418           21,132,018
        4              3         21,132,018           21,163,953
        5              4         21,163,953           21,191,900
    3、SCN与oracle数据库恢复的关系
    SCN号与oracle数据库恢复过程有着密切的关系,只有很好地理解了这层关系,才能深刻地理解恢复的原理,从而才能很好地解决这方面的问题。
    CKPT进程在checkpoint发生时,将当时的SCN号写入数据文件头和控制文件,同时通知DBWR进程将数据块写到数据文件。CKPT进程也会在控制文件中记录RBA(redo byte address),以标志Recovery需要从日志中哪个地方开始。
    1、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的时候。
    2、SCN与数据库启动
    在数据库启动过程中,当System Checkpoint SCN、Datafile Checkpoint SCN和Start SCN号都相同时,数据库可以正常启动,不需要做media recovery.三者当中有一个不同时,则需要做media recovery。如果在启动的过程中,End SCN号为NULL,则需要做instance recovery。ORACLE在启动过程中首先检查是否需要media recovery,然后再检查是否需要instance recovery。
    3、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号,可以区分控制文件是否是当前的控制文件。
    4、Recovery database using backup controlfile
    当有一个Start SCN号超过了System Checkpoit SCN号时,则说明控制文件不是当前的控制文件,因此在做recovery时需要采用using backup controlfile。这是为什么需要记录SystemCheckpoint SCN的原因之一。
    这里需要一提的是,当重建控制文件的时候,System Checkpoint SCN为0,Datafile Checkpoint SCN的数据来自于Start SCN。根据上述的描述,此时需要采用using backup controlfile做recovery。
    重建控制文件方式分两种(resetlogs和noresetlogs):
    1) 使用resetlogs选项时,System Checkpoint SCN为被归为0,而其中记录的各个数据文件的Datafile Checkpoint SCN则来自于Start SCN(也就是说可能会从冷备份的数据文件的数据文件头中获取)。根据上述的描述,此时需要采用using backup controlfile做recovery.因此情况是System Checkpoint SCN=0 < Start SCN = Datafile Checkpoint SCN。
    2) 使用noresetlogs选项时,有一个前提就是:一定要有online redo log的存在。否则就要使用resetlogs选项。这个时候控制文件重建好时,其system checkpoint SCN=Datafile Checkpoint SCN=Lastest Checkpoint SCN in online redo log,我们可以看到Datafile Checkpoint SCN并没有从Start SCN中读取。而是读取了最新的日志文件中的SCN作为自己的数据。此时重建的控制文件在恢复中的作用跟最新的控制文件类似,System Checkpoint SCN(已经读取最新的redo log的checkpoint SCN信息)可能会>Start SCN(因为数据文件可能会从冷备份中恢复),恢复时就不需要加using backup controlfile子句了。
    关于backup controlfile的补充:
    backup controlfile只有备份时刻的archive log信息,并没有DB crash时刻的archive log信息,所以并不会自动应用online redo log,而是提示找不到序号为Lastest Archive log sequence + 1的archive log。尽管你可以手动指定online redo log来实现完全恢复,但因为一旦使用了using backup controlfile子句,Oracle就视为不完全恢复,必须open resetlogs。实际上,假如你有旧的控制文件又不想resetlogs,那很简单,使用旧的控制文件mount然后backup to trace,然后手工创建控制文件,使用reuse database ... noresetlogs这样就可以recover database自动恢复并open database而不用resetlogs了(切记:必须有所有的online redo logs才可以这样!)。备份的控制文件不能自动进行完全恢复,可以手工apply日志进行完全恢复。
    5、重建自动进行完全恢复示例
    背景:
    Oracle 11.2.0.1.0
    Linux Enterprise RedHat 5
    采用rman做热备,在备份期间,做不少事务,同时做alter system checkpoint:
    [oracle@localhost software]$ rman target /
    Recovery Manager: Release 11.2.0.1.0 - Production on 星期二 3月 20 15:15:38 2012
    Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.
    connected to target database: ORCL (DBID=1284637334)
    RMAN> run {
    2> allocate channel c1 type disk;
    3> backup database filesperset 3 format '/software/full_%p_%t.bak';
    4> release channel c1;
    5> }
    allocated channel: c1
    channel c1: SID=35 device type=DISK

    Starting backup at 20-3月 -12
    channel c1: starting compressed full datafile backup set
    channel c1: specifying datafile(s) in backup set
    input datafile file number=00005 name=/home/oracle/oradata/users02.dbf
    input datafile file number=00001 name=/opt/oracle/oradata/orcl/system01.dbf
    input datafile file number=00003 name=/opt/oracle/oradata/orcl/undotbs01.dbf
    channel c1: starting piece 1 at 20-3月 -12
    channel c1: finished piece 1 at 20-3月 -12
    piece handle=/software/full_1_778432785.bak tag=TAG20120320T151945 comment=NONE
    channel c1: backup set complete, elapsed time: 00:02:57
    channel c1: starting compressed full datafile backup set
    channel c1: specifying datafile(s) in backup set
    input datafile file number=00004 name=/home/oracle/oradata/users01.dbf
    input datafile file number=00002 name=/home/oracle/oradata/sysaux01.dbf
    input datafile file number=00006 name=/home/oracle/oradata/undotbs02.dbf
    channel c1: starting piece 1 at 20-3月 -12
    channel c1: finished piece 1 at 20-3月 -12
    piece handle=/software/full_1_778432963.bak tag=TAG20120320T151945 comment=NONE
    channel c1: backup set complete, elapsed time: 00:01:45
    Finished backup at 20-3月 -12

    Starting Control File and SPFILE Autobackup at 20-3月 -12
    piece handle=/home/oracle/dbbackup/ctl_20120320_c-1284637334-20120320-02 comment=NONE
    Finished Control File and SPFILE Autobackup at 20-3月 -12

    released channel: c1
    注:在这个备份角本里面我们加了filesperset 3。这样将整个数据库分成两个备份集。这样还原出来的数据文件其checkpoint_change#将不一样。否则由于数据库数据文件不多,都将包含在一个备份集中,这样即使在备份中做insert操作和alter system checkpoint也不会产生不同的checkpoint_change#。因为rman备份是将一个备份集中的文件同时备份的。而checkpoint_change#是存放在数据文件头部的,这样备份这些数据文件的头部的时间将是很快的。
    然后模拟还原数据库:
    sys@ORCL> shutdown immediate
    Database closed.
    Database dismounted.
    ORACLE instance shut down.
    sys@ORCL> startup mount;
    ORACLE instance started.
    Total System Global Area  263049216 bytes
    Fixed Size                    2212448 bytes
    Variable Size                  234884512 bytes
    Database Buffers           20971520 bytes
    Redo Buffers                    4980736 bytes
    Database mounted.
    RMAN> run {
    2> allocate channel c1 type disk;
    3> restore database;
    4> release channel c1;
    5> }
    using target database control file instead of recovery catalog
    allocated channel: c1
    channel c1: SID=21 device type=DISK

    Starting restore at 20-3月 -12

    channel c1: starting datafile backup set restore
    channel c1: specifying datafile(s) to restore from backup set
    channel c1: restoring datafile 00001 to /opt/oracle/oradata/orcl/system01.dbf
    channel c1: restoring datafile 00003 to /opt/oracle/oradata/orcl/undotbs01.dbf
    channel c1: restoring datafile 00005 to /home/oracle/oradata/users02.dbf
    channel c1: reading from backup piece /software/full_1_778432785.bak
    channel c1: piece handle=/software/full_1_778432785.bak tag=TAG20120320T151945
    channel c1: restored backup piece 1
    channel c1: restore complete, elapsed time: 00:02:27
    channel c1: starting datafile backup set restore
    channel c1: specifying datafile(s) to restore from backup set
    channel c1: restoring datafile 00002 to /home/oracle/oradata/sysaux01.dbf
    channel c1: restoring datafile 00004 to /home/oracle/oradata/users01.dbf
    channel c1: restoring datafile 00006 to /home/oracle/oradata/undotbs02.dbf
    channel c1: reading from backup piece /software/full_1_778432963.bak
    channel c1: piece handle=/software/full_1_778432963.bak tag=TAG20120320T151945
    channel c1: restored backup piece 1
    channel c1: restore complete, elapsed time: 00:01:55
    Finished restore at 20-3月 -12

    released channel: c1
    sys@ORCL> select checkpoint_change# from v$database;
    CHECKPOINT_CHANGE#
    ------------------
              21674485
    sys@ORCL> select file#,checkpoint_change# from v$datafile;
      FILE#   CHECKPOINT_CHANGE#
    ----------    ------------------
             1             21674485
             2             21674485
             3             21674485
             4             21674485
             5             21674485
             6             21674485
    6 rows selected.
    sys@ORCL> select file#,checkpoint_change# from v$datafile_header;
         FILE# CHECKPOINT_CHANGE#
    ---------- ------------------
             1             21673534
             2             21673648
             3             21673534
             4             21673648
             5             21673534
             6             21673648
    6 rows selected.
    从这里可以看出,显然是需要做media recovery的。正常情况下,还需要做instance recovery。当然由于没有在线日志,所以只能做resetlogs。
    1) 有归档日志存
    若有归档日志在,则只需要做一个recover database until cancel;然后即可alter database open resetlogs;
    sys@ORCL> recover database until cancel;
    ORA-00279: 更改 21673534 (在 03/20/2012 15:19:46 生成) 对于线程 1 是必需的
    ORA-00289: 建议: /opt/oracle/flash_recovery_area/ORCL/archivelog/2012_03_20/o1_mf_1_22_7pjh1ss0_.arc
    ORA-00280: 更改 21673534 (用于线程 1) 在序列 #22 中
    Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
    auto
    ORA-00279: 更改 21674803 (在 03/20/2012 16:13:45 生成) 对于线程 1 是必需的
    ORA-00289: 建议: /opt/oracle/flash_recovery_area/ORCL/archivelog/2012_03_20/o1_mf_1_23_7pjh1vz3_.arc
    ORA-00280: 更改 21674803 (用于线程 1) 在序列 #23 中
    ORA-00278: 此恢复不再需要日志文件 '/opt/oracle/flash_recovery_area/ORCL/archivelog/2012_03_20/o1_mf_1_22_7pjh1ss0_.arc'
    ORA-00279: 更改 21674806 (在 03/20/2012 16:13:47 生成) 对于线程 1 是必需的
    ORA-00289: 建议: /opt/oracle/flash_recovery_area/ORCL/archivelog/2012_03_20/o1_mf_1_24_7pjh1z5h_.arc
    ORA-00280: 更改 21674806 (用于线程 1) 在序列 #24 中
    ORA-00278: 此恢复不再需要日志文件 '/opt/oracle/flash_recovery_area/ORCL/archivelog/2012_03_20/o1_mf_1_23_7pjh1vz3_.arc'
    ORA-00279: 更改 21674809 (在 03/20/2012 16:13:51 生成) 对于线程 1 是必需的
    ORA-00289: 建议: /opt/oracle/flash_recovery_area/ORCL/archivelog/2012_03_20/o1_mf_1_25_%u_.arc
    ORA-00280: 更改 21674809 (用于线程 1) 在序列 #25 中
    ORA-00278: 此恢复不再需要日志文件 '/opt/oracle/flash_recovery_area/ORCL/archivelog/2012_03_20/o1_mf_1_24_7pjh1z5h_.arc'
    ORA-00308: 无法打开归档日志 '/opt/oracle/flash_recovery_area/ORCL/archivelog/2012_03_20/o1_mf_1_25_%u_.arc'
    ORA-27037: 无法获得文件状态
    Linux-x86_64 Error: 2: No such file or directory
    Additional information: 3
    sys@ORCL> alter database open resetlogs;
    Database altered.
    2) 无归档日志
    如果没有归档日志,由于restore出来是没有在线日志的。如果v$datafile_header中checkpoint_change#是相同的,此时由于控制文件中checkpoint_change#比数据文件头中要高,所以数据库还是需要做media recovery。
    此时重建控制文件还是一样的,因为重建控制文件后,在控制文件中checkpoint_change#为0,与文件头的checkpoint_change#还是不一样,还需要media recovery。且由于控制文件中checkpoint_change#比文件头中要高,所以做recover时还需要加上using backup controlfile。
    注:这时由于没有在线日志,所以重建控制文件需要将RESETLOGS改成RESETLOGS才可以创建成功,否则会报以下错误:
    ORA-01565: error in identifying file 'D:ORACLE8IORADATAORA8IREDO01.LOG'
    ORA-27041: unable to open file
    此时SCN号信息如下:
    sys@ORCL> select CHECKPOINT_CHANGE#,CONTROLFILE_CHANGE# from v$database;
    CHECKPOINT_CHANGE#  CONTROLFILE_CHANGE#
    ------------------             -------------------
      0                          0
    此时由于没有归档日志和在线日志,无法做recovery。所以也就无法做alter database open Resetlogs了。此时可以加上_allow_resetlogs_corruption隐含参数,然后就可以alter database open resetlogs将数据库打开了。
    当然如果v$datafile_header中checkpoint_change#是不相同的,那么此时就没有什么常归有效的办法能将数据库打开了。如果相差不多,加上隐含参数_allow_resetlogs_corruption,然后alter database open resetlogs还是有可能可以打开的。这个参数oracle是不建议加的,且加上这个参数也只是有可能可以打开。这个参数是以最oldest的SCN将数据库打开,所以最好system数据文件的SCN号是最oldest的,否则容易产生大量的600号错误。

    四、测试用例
    1、查看正常关闭/启动过程
    数据库正常关闭,在数据库启动过程中:当System Checkpoint SCN、Datafile Checkpoint SCN和Start SCN号都相同时,数据库可以正常启动
    sys@ORCL> shutdown normal
    Database closed.
    Database dismounted.
    ORACLE instance shut down.
    sys@ORCL> startup mount
    ORACLE instance started.

    Total System Global Area  263049216 bytes
    Fixed Size                    2212448 bytes
    Variable Size                  234884512 bytes
    Database Buffers           20971520 bytes
    Redo Buffers                    4980736 bytes
    Database mounted.
    sys@ORCL> select checkpoint_change# from v$database;
    CHECKPOINT_CHANGE#
    ------------------
    21677208
    sys@ORCL> select checkpoint_change# from v$datafile;
    CHECKPOINT_CHANGE#
    ------------------
              21677208
              21677208
              21677208
              21677208
              21677208
              21677208
    6 rows selected.
    SYS@ORCL> select checkpoint_change#,last_change# from v$datafile;
    CHECKPOINT_CHANGE# LAST_CHANGE#
    ------------------ ------------
              21677208     21677208
              21677208     21677208
              21677208     21677208
              21677208     21677208
              21677208     21677208
              21677208     21677208
    6 rows selected.
    SYS@ORCL> select checkpoint_change# from v$datafile_header;
    CHECKPOINT_CHANGE#
    ------------------
              21677208
              21677208
              21677208
              21677208
              21677208
              21677208
    6 rows selected.
    SYS@ORCL> alter database open;
    Database altered.
    看此时database的日志文件:
    Tue Mar 20 17:07:10 2012
    ALTER DATABASE   MOUNT
    Successful mount of redo thread 1, with mount id 1306217406
    Allocated 3981120 bytes in shared pool for flashback generation buffer
    Starting background process RVWR
    Tue Mar 20 17:07:14 2012
    RVWR started with pid=20, OS id=23412
    Database mounted in Exclusive Mode
    Lost write protection disabled
    Completed: ALTER DATABASE   MOUNT
    Tue Mar 20 17:11:27 2012
    alter database open
    Tue Mar 20 17:11:27 2012
    LGWR: STARTING ARCH PROCESSES
    Tue Mar 20 17:11:28 2012
    ARC0 started with pid=21, OS id=23559
    ARC0: Archival started
    LGWR: STARTING ARCH PROCESSES COMPLETE
    ARC0: STARTING ARCH PROCESSES
    Tue Mar 20 17:11:29 2012
    ARC1 started with pid=22, OS id=23561
    Thread 1 opened at log sequence 1
      Current log# 1 seq# 1 mem# 0: /opt/oracle/oradata/orcl/redo01.log
    Successful open of redo thread 1
    MTTR advisory is disabled because FAST_START_MTTR_TARGET is not set
    Tue Mar 20 17:11:29 2012
    SMON: enabling cache recovery
    Tue Mar 20 17:11:29 2012
    ARC2 started with pid=23, OS id=23563
    ARC1: Archival started
    ARC2: Archival started
    ARC1: Becoming the 'no FAL' ARCH
    ARC1: Becoming the 'no SRL' ARCH
    Tue Mar 20 17:11:29 2012
    ARC3 started with pid=24, OS id=23565
    ARC2: Becoming the heartbeat ARCH
    Successfully onlined Undo Tablespace 2.
    Verifying file header compatibility for 11g tablespace encryption..
    Verifying 11g file header compatibility for tablespace encryption completed
    SMON: enabling tx recovery
    Database Characterset is ZHS16GBK
    No Resource Manager plan active
    replication_dependency_tracking turned off (no async multimaster replication found)
    ARC3: Archival started
    ARC0: STARTING ARCH PROCESSES COMPLETE
    Starting background process QMNC
    Tue Mar 20 17:11:32 2012
    QMNC started with pid=25, OS id=23567
    Completed: alter database open
    可以看到数据库正常启动没有做任何instance recovery,和media recovery。
    2、END SCN为NULL
    如果在启动的过程中,End SCN号为NULL,则需要做instance recovery. 这种情况一般是数据库不正常关闭导致  测试如下:
    SYS@ORCL> shutdown abort
    ORACLE instance shut down.
    SYS@ORCL> startup mount
    &not;ORACLE instance started.
    Total System Global Area  263049216 bytes
    Fixed Size                    2212448 bytes
    Variable Size                  234884512 bytes
    Database Buffers           20971520 bytes
    Redo Buffers                    4980736 bytes
    Database mounted.
    SYS@ORCL> select checkpoint_change# from v$datafile_header;
    CHECKPOINT_CHANGE#
    ------------------
              21677211
              21677211
              21677211
              21677211
              21677211
              21677211
    6 rows selected.
    SYS@ORCL> select checkpoint_change#,last_change# from v$datafile;
    CHECKPOINT_CHANGE# LAST_CHANGE#
    ------------------ ------------
              21677211
              21677211
              21677211
              21677211
              21677211
              21677211        ----可以看到last_change# 下面的内容为NULL
    6 rows selected.
    SYS@ORCL> select checkpoint_change# from v$database;
    CHECKPOINT_CHANGE#
    ------------------
          21677211
    SYS@ORCL> alter database open;  -------在这个过程中做了实例恢复
    Database altered.
    此时日志文件中记录如下:
    Tue Mar 20 17:15:03 2012
    Shutting down instance (abort)
    License high water mark = 3
    USER (ospid: 23413): terminating the instance
    Instance terminated by USER, pid = 23413
    Tue Mar 20 17:15:04 2012
    Instance shutdown complete
    Tue Mar 20 17:15:32 2012
    Starting ORACLE instance (normal)
    LICENSE_MAX_SESSION = 0
    LICENSE_SESSIONS_WARNING = 0
    Picked latch-free SCN scheme 3
    Using LOG_ARCHIVE_DEST_1 parameter default value as USE_DB_RECOVERY_FILE_DEST
    Autotune of undo retention is turned on.
    IMODE=BR
    ILAT =27
    LICENSE_MAX_USERS = 0
    SYS auditing is disabled
    Starting up:
    Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
    With the Partitioning, OLAP, Data Mining and Real Application Testing options.
    Using parameter settings in server-side spfile /opt/oracle/product/db_1/dbs/spfileorcl.ora
    System parameters with non-default values:
      processes                = 150
      sga_max_size             = 252M
      sga_target               = 152M
      control_files            = "/opt/oracle/oradata/orcl/control01.ctl"
      control_files            = "/opt/oracle/flash_recovery_area/orcl/control02.ctl"
      db_block_size            = 8192
      compatible               = "11.2.0.0.0"
      db_recovery_file_dest    = "/opt/oracle/flash_recovery_area"
      db_recovery_file_dest_size= 3882M
      db_flashback_retention_target= 30
      undo_tablespace          = "UNDOTBS1"
      remote_login_passwordfile= "EXCLUSIVE"
      db_domain                = ""
      dispatchers              = "(PROTOCOL=TCP) (SERVICE=orclXDB)"
      job_queue_processes      = 1000
      audit_file_dest          = "/opt/oracle/admin/orcl/adump"
      audit_trail              = "DB_EXTENDED"
      db_name                  = "orcl"
      open_cursors             = 300
      pga_aggregate_target     = 96M
      diagnostic_dest          = "/opt/oracle"
    Tue Mar 20 17:15:33 2012
    PMON started with pid=2, OS id=23761
    Tue Mar 20 17:15:33 2012
    VKTM started with pid=3, OS id=23763 at elevated priority
    VKTM running at (10)millisec precision with DBRM quantum (100)ms
    Tue Mar 20 17:15:33 2012
    GEN0 started with pid=4, OS id=23767
    Tue Mar 20 17:15:33 2012
    DIAG started with pid=5, OS id=23769
    Tue Mar 20 17:15:33 2012
    DBRM started with pid=6, OS id=23771
    Tue Mar 20 17:15:33 2012
    PSP0 started with pid=7, OS id=23773
    Tue Mar 20 17:15:33 2012
    DIA0 started with pid=8, OS id=23775
    Tue Mar 20 17:15:33 2012
    MMAN started with pid=9, OS id=23777
    Tue Mar 20 17:15:33 2012
    DBW0 started with pid=10, OS id=23779
    Tue Mar 20 17:15:33 2012
    LGWR started with pid=11, OS id=23781
    Tue Mar 20 17:15:33 2012
    CKPT started with pid=12, OS id=23783
    Tue Mar 20 17:15:33 2012
    SMON started with pid=13, OS id=23785
    Tue Mar 20 17:15:33 2012
    RECO started with pid=14, OS id=23787
    Tue Mar 20 17:15:33 2012
    MMON started with pid=15, OS id=23789
    Tue Mar 20 17:15:33 2012
    MMNL started with pid=16, OS id=23791
    starting up 1 dispatcher(s) for network address '(ADDRESS=(PARTIAL=YES)(PROTOCOL=TCP))'...
    starting up 1 shared server(s) ...
    ORACLE_BASE from environment = /opt/oracle
    Tue Mar 20 17:15:33 2012
    ALTER DATABASE   MOUNT
    Successful mount of redo thread 1, with mount id 1306255541
    Allocated 3981120 bytes in shared pool for flashback generation buffer
    Starting background process RVWR
    Tue Mar 20 17:15:37 2012
    RVWR started with pid=20, OS id=23802
    Database mounted in Exclusive Mode
    Lost write protection disabled
    Completed: ALTER DATABASE   MOUNT
    Tue Mar 20 17:24:26 2012
    alter database open
    Beginning crash recovery of 1 threads
    Started redo scan
    Completed redo scan
    read 68 KB redo, 47 data blocks need recovery
    Started redo application at
    Thread 1: logseq 1, block 6600
    Recovery of Online Redo Log: Thread 1 Group 1 Seq 1 Reading mem 0  --当前日志文件做实例恢复instance recovery。
      Mem# 0: /opt/oracle/oradata/orcl/redo01.log
    Completed redo application of 0.04MB
    Completed crash recovery at
    Thread 1: logseq 1, block 6737, scn 21697600
    47 data blocks read, 47 data blocks written, 68 redo k-bytes read
    Tue Mar 20 17:24:27 2012
    LGWR: STARTING ARCH PROCESSES
    Tue Mar 20 17:24:27 2012
    ARC0 started with pid=21, OS id=24121
    ARC0: Archival started
    LGWR: STARTING ARCH PROCESSES COMPLETE
    ARC0: STARTING ARCH PROCESSES
    Tue Mar 20 17:24:28 2012
    ARC1 started with pid=22, OS id=24123
    Tue Mar 20 17:24:28 2012
    ARC2 started with pid=23, OS id=24125
    Thread 1 advanced to log sequence 2 (thread open)
    ARC1: Archival started
    ARC2: Archival started
    ARC1: Becoming the 'no FAL' ARCH
    ARC1: Becoming the 'no SRL' ARCH
    ARC2: Becoming the heartbeat ARCH
    Tue Mar 20 17:24:28 2012
    ARC3 started with pid=24, OS id=24127
    Thread 1 opened at log sequence 2
      Current log# 2 seq# 2 mem# 0: /opt/oracle/oradata/orcl/redo02.log
    Successful open of redo thread 1
    MTTR advisory is disabled because FAST_START_MTTR_TARGET is not set
    Successfully onlined Undo Tablespace 2.
    Verifying file header compatibility for 11g tablespace encryption..
    Verifying 11g file header compatibility for tablespace encryption completed
    Tue Mar 20 17:24:28 2012
    SMON: enabling cache recovery
    SMON: enabling tx recovery
    Database Characterset is ZHS16GBK
    No Resource Manager plan active
    replication_dependency_tracking turned off (no async multimaster replication found)
    Starting background process QMNC
    Tue Mar 20 17:24:29 2012
    QMNC started with pid=25, OS id=24129
    Completed: alter database open.
    3、SCN不同时恢复
    oracle在启动过程中三者当中有一个不同时,则需要做media recovery,步骤是:首先检查是否需要media recovery,然后再检查是否需要instance recovery.测试如下;
    SYS@ORCL> alter tablespace tools begin backup;
    Tablespace altered.
    此时拷贝tools01.dbf 到别处
    SYS@ORCL> create table tools (id number) tablespace tools;
    Table created.
    SYS@ORCL> alter tablespace tools end backup;
    Tablespace altered.
    SYS@ORCL> create table tools (id number) tablespace tools;
    Table created.
    SYS@ORCL> insert into tools values(10);
    1 row created.
    SYS@ORCL> commit;
    Commit complete.
    SYS@ORCL> shutdown abort;
    ORACLE instance shut down.
    拷贝 tools01.dbf 回来
    SYS@ORCL> startup mount;
    ORACLE instance started.
    Total System Global Area  101785252 bytes
    Fixed Size                   454308 bytes
    Variable Size              75497472 bytes
    Database Buffers           25165824 bytes
    Redo Buffers                 667648 bytes
    Database mounted.
    SYS@ORCL> select checkpoint_change# from v$database;
    CHECKPOINT_CHANGE#
    ------------------
         251084
    SYS@ORCL> select checkpoint_change#,last_change# from v$datafile;
    CHECKPOINT_CHANGE# LAST_CHANGE#
    ------------------ ------------
                251084
                251084
                251084
                251084
                251084
    SYS@ORCL> select checkpoint_change# from v$datafile_header;
    CHECKPOINT_CHANGE#
    ------------------
                251084
                251084
                251084
               250867
                251084
    可以看到 数据文件头的SCN为250867,小于数据文件SCN 251084,和系统检查好SCN 251084。
    SYS@ORCL> alter database open;    --提示做介质恢复
    alter database open
    *
    ERROR at line 1:
    ORA-01113: file 4 needs media recovery
    ORA-01110: data file 4: 'D:\ORACLE\ORADATA\PUBTEST\TOOLS01.DBF'
    SYS@ORCL> recover datafile 4;
    Media recovery complete.
    SYS@ORCL> alter database open;
    Database altered.
    此时日志文件中内容如下:
    ALTER DATABASE RECOVER  datafile 4
    Media Recovery Datafile: 4
    Media Recovery Start
    Starting datafile 4 recovery in thread 1 sequence 16
    Datafile 4: 'D:\ORACLE\ORADATA\PUBTEST\TOOLS01.DBF'
    Media Recovery Log
    Recovery of Online Redo Log: Thread 1 Group 1 Seq 16 Reading mem 0
    Mem# 0 errs 0: D:\ORACLE\ORADATA\PUBTEST\REDO01.LOG
    Recovery of Online Redo Log: Thread 1 Group 2 Seq 17 Reading mem 0
    Mem# 0 errs 0: D:\ORACLE\ORADATA\PUBTEST\REDO02.LOG
    Media Recovery Complete
    Completed: ALTER DATABASE RECOVER  datafile 4
    Tue Mar 18 09:48:50 2008
    alter database open
    Tue Mar 18 09:48:50 2008
      Beginning crash recovery of 1 threads
    Tue Mar 18 09:48:50 2008
    Started first pass scan
    Tue Mar 18 09:48:52 2008
    Completed first pass scan
    83 redo blocks read, 50 data blocks need recovery
    Tue Mar 18 09:48:52 2008
    Started recovery at
    Thread 1: logseq 17, block 29, SCN 0.0
    Recovery of Online Redo Log: Thread 1 Group 2 Seq 17 Reading mem 0
      Mem# 0 errs 0: D:\ORACLE\ORADATA\PUBTEST\REDO02.LOG
    Tue Mar 18 09:48:52 2008
    Completed redo application
    Tue Mar 18 09:48:52 2008
    Ended recovery at
    Thread 1: logseq 17, block 112, SCN 0.271189
    50 data blocks read, 50 data blocks written, 83 redo blocks read
    Crash recovery completed successfully
    Tue Mar 18 09:48:53 2008
    LGWR: Primary database is in CLUSTER CONSISTENT mode
    Thread 1 advanced to log sequence 18
    Thread 1 opened at log sequence 18
      Current log# 3 seq# 18 mem# 0: D:\ORACLE\ORADATA\PUBTEST\REDO03.LOG
    Successful open of redo thread 1.
    Tue Mar 18 09:48:54 2008
    SMON: enabling cache recovery
    Tue Mar 18 09:48:54 2008
    ARC0: Evaluating archive   log 2 thread 1 sequence 17
    ARC0: Beginning to archive log 2 thread 1 sequence 17
    Creating archive destination LOG_ARCHIVE_DEST_1: 'D:\ORACLE\ORA92\RDBMS\ARC00017.001'
    ARC0: Completed archiving  log 2 thread 1 sequence 17
    Tue Mar 18 09:48:54 2008
    Undo Segment 1 Onlined
    Undo Segment 2 Onlined
    Undo Segment 3 Onlined
    Undo Segment 4 Onlined
    Undo Segment 5 Onlined
    Undo Segment 6 Onlined
    Undo Segment 7 Onlined
    Undo Segment 8 Onlined
    Undo Segment 9 Onlined
    Undo Segment 10 Onlined
    Successfully onlined Undo Tablespace 1.
    Tue Mar 18 09:48:54 2008
    SMON: enabling tx recovery
    Tue Mar 18 09:48:54 2008
    Database Characterset is ZHS16GBK
    replication_dependency_tracking turned off (no async multimaster replication found)
    Completed: alter database open
    五、深入分析checkpoint_change#
    1、系统检查点(记录在控制文件中)
    SYS@ORCL> select checkpoint_change# from v$database;
    CHECKPOINT_CHANGE#
    ------------------
              21728618
    2、数据文件检查点(记录在控制文件中)
    SYS@ORCL> select file#,checkpoint_change#,last_change# from v$datafile;
         FILE# CHECKPOINT_CHANGE# LAST_CHANGE#
    ---------- ------------------ ------------
             1             21728618
             2             21728618
             3             21728618
             4             21728618
             5             21728618
             6             21728618
    6 rows selected.
    3、数据文件头检查点(记录在数据文件中)
    SYS@ORCL> select file#,checkpoint_change# from v$datafile_header;
         FILE# CHECKPOINT_CHANGE#
    ---------- ------------------
             1             21728618
             2             21728618
             3             21728618
             4             21728618
             5             21728618
             6             21728618
    6 rows selected.
    以上三个checkpoint_change#要一致(只读、脱机表空间除外),数据库才能正常打开。否则会需要进行一步的处理。正常关库时,会生成新的检查点,写入上述三个checkpoint_change#,同时数据文件中的last_change#也会记录下该检查点,也就是说三个checkpoint_change#与last_change#记录着同一个值。通过以下SQL可以证明:
    SYS@ORCL> shutdown immediate
    Database closed.
    Database dismounted.
    ORACLE instance shut down.
    SYS@ORCL> startup mount
    ORACLE instance started.
    Total System Global Area  263049216 bytes
    Fixed Size                    2212448 bytes
    Variable Size                  230690208 bytes
    Database Buffers           25165824 bytes
    Redo Buffers                    4980736 bytes
    Database mounted.
    SYS@ORCL> select file#,checkpoint_change#,last_change# from v$datafile;
         FILE# CHECKPOINT_CHANGE# LAST_CHANGE#
    ---------- ------------------ ------------
             1             21732562          21732562
             2             21732562          21732562
             3             21732562          21732562
             4             21732562          21732562
             5             21732562          21732562
             6             21732562          21732562
    6 rows selected.
    SYS@ORCL> select checkpoint_change# from v$database;
    CHECKPOINT_CHANGE#
    ------------------
              21732562
    SYS@ORCL> select file#,checkpoint_change# from v$datafile_header;
         FILE# CHECKPOINT_CHANGE#
    ---------- ------------------
             1             21732562
             2             21732562
             3             21732562
             4             21732562
             5             21732562
             6             21732562
    6 rows selected.
    数据库成功打开后,数据文件中的last_change#会被清空。正常关库时,再重新下最后的检查点。shutdown abort关库,这个值是空的(感兴趣可自行验证),此时数据库需要进行实例恢复(不需要用户干预),恢复后数据库才正常打开。checkpoint_change#、last_change#实际上全部来自于SCN,可以通过下面的语句验证:
    sys@ORCL> alter database open;
    Database altered.
    SYS@ORCL> select dbms_flashback.get_system_change_number from dual;
    GET_SYSTEM_CHANGE_NUMBER
    ------------------------
                    21732835
    使用查询系统SCN号的函数,可以发现checkpoint_change#与之是接近的。SCN有很多触发的条件,可能不会特别接近。下面举几个全备后恢复的例子,以及相关场景下checkpoint_change#的情况。
    问题1:数据文件损坏的恢复
    此时控制文件中记录的checkpoint_change#比数据文件头中记录的要大,数据库需要介质或者实例恢复。
    SYS@ORCL> startup
    Database mounted.
    ORA-01113: file 1 needs media recovery
    ORA-01110: data file 1: '/u02/oradata/orcl/system01.dbf'
    恢复一下,数据库就可以打开了。
    SYS@ORCL> recover database;
    ORA-00279: change 539624 generated at 10/18/2011 08:27:31 needed for thread 1
    ORA-00289: suggestion : /u02/oradata/orcl/arc/1_5_764840495.dbf
    ORA-00280: change 539624 for thread 1 is in sequence #5


    Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
    auto
    ORA-00279: change 540768 generated at 10/18/2011 12:17:11 needed for thread 1
    ORA-00289: suggestion : /u02/oradata/orcl/arc/1_6_764840495.dbf
    ORA-00280: change 540768 for thread 1 is in sequence #6
    ORA-00278: log file '/u02/oradata/orcl/arc/1_5_764840495.dbf' no longer needed for this recovery
    Log applied.
    Media recovery complete.
    SYS@ORCL> alter database open;
    Database altered.
    问题2:控制文件损坏的恢复
    如果控制文件损坏,使用备份的控制文件是无法直接打开数据库的。
    SYS@ORCL> startup
    Database mounted.
    ORA-01122: database file 1 failed verification check
    ORA-01110: data file 1: '/u02/oradata/orcl/system01.dbf'
    ORA-01207: file is more recent than controlfile - old controlfile
    会提示数据文件与控制文件新,实际上就是控制文件中记录的checkpoint_change#比数据文件头中的checkpoint_change#要小,这种情况是不能打开数据库的。但数据可以启动到mount状态,此时可以用命令:
    SYS@ORCL> alter database backup controlfile to trace;
    生成一个控制文件的脚本,在udump目录中。使用该脚本可以重建控制文件,进行实例恢复后或打开数据库。如果没有备份的控制文件,数据库只能打开的nomount状态,不能获取重建控制文件的脚本。如果数据库不太复杂,可以手写一个。
    问题3:数据文件、控制文件全部损坏
    当然都有备份,日志是好的。恢复数据文件、控制文件后,数据库仍然是无法打开的。
    SYS@ORCL> startup
    Database mounted.
    ORA-00314: log 1 of thread 1, expected sequence#  doesn't match
    ORA-00312: online log 1 thread 1: '/u02/oradata/orcl/redo01.log'
    提示的意思也就是日志中的检查点比较控制文件中记录的大。
    SYS@ORCL> select checkpoint_change# from v$datafile;
    CHECKPOINT_CHANGE#
    ------------------
                539624
                539624
                539624
                539624
                539624
    SYS@ORCL> select checkpoint_change# from v$datafile_header;
    CHECKPOINT_CHANGE#
    ------------------
                539624
                539624
                539624
                539624
                539624
    SYS@ORCL> select checkpoint_change# from v$database;
    CHECKPOINT_CHANGE#
    ------------------
                539624
    SYS@ORCL> select * from v$log;
    GROUP#  THREAD#  SEQUENCE#  BYTES  MEMBERS  ARC  STATUS FIRST_CHANGE#  FIRST_TIM
    ---------- ---------- ---------- ---------- ---------- --- ---------------- ------------- ---------
    1          1          5   10485760          1 NO  CURRENT                 539571 18-OCT-11
    2          1          4   10485760          1 YES INACTIVE                539116 18-OCT-11
    3          1          3   10485760          1 YES INACTIVE                537456 18-OCT-11
    此时可以使用下面的命令恢复数据库:
    SYS@ORCL> recover database using backup controlfile;
    恢复成功后,v$database中记录的checkpoint_change#并未发生变化。
    SYS@ORCL> select checkpoint_change# from v$datafile;
    CHECKPOINT_CHANGE#
    ------------------
                602574
                602574
                602574
                602574
                602574
    SYS@ORCL> select checkpoint_change# from v$datafile_header;
    CHECKPOINT_CHANGE#
    ------------------
                602574
                602574
                602574
                602574
                602574
    SYS@ORCL>  select checkpoint_change# from v$database;
    CHECKPOINT_CHANGE#
    ------------------
                539624
    因为不一致,所以数据库仍然打不开:
    SYS@ORCL> alter database open;
    alter database open
    *
    ERROR at line 1:
    ORA-01589: must use RESETLOGS or NORESETLOGS option for database open
    SYS@ORCL> alter database open resetlogs;
    alter database open resetlogs
    *
    ERROR at line 1:
    ORA-01113: file 1 needs media recovery
    ORA-01110: data file 1: '/u02/oradata/orcl/system01.dbf'
    此时的情况类似于问题2,解决办法也相同。shutdown abort,startup nomount,重建控制文件,recover database,alter database open;
    问题4、冷备过后新建的数据文件损坏,且无备份
    这种情况的处理办法:
    SYS@ORCL> restore’备份的数据文件’;
    SYS@ORCL> startup;
    会提示无法定位数据文件,数据库无法打开,alter database create datafile提示无法定位的数据文件名称;此时查看checkpoint_change#,会发现新建的与其它的不相同。
    SYS@ORCL> set autorecovery on
    SYS@ORCL> recover database;
    SYS@ORCL> alter database open;

  • 相关阅读:
    一篇文章高效定位iframe
    URL与视图函数的映射
    include标签—引用文件路径
    UnitTest单元测试框架解析【实用篇】
    【案例演练】测试器与模板继承
    2招带你快速获取响应头与发送头
    dede网站如何更新地图sitemap.html
    申请微信小程序流程步骤
    phpstudy本地配置--dede织梦网
    stylus样式开发的使用----vue
  • 原文地址:https://www.cnblogs.com/datalife/p/2542014.html
Copyright © 2011-2022 走看看