SCN是oracle掛在牆上的時鐘。早上起床,曰“起床SCN”;吃早餐,名“早餐SCN”;出門上班,稱之為“出門SCN”。我們的任何活動,都會對應一個SCN。我們可藉助oracle內部的一個包來擷取系統的SCN(注意:這裡只是系統的scn,因為,oracle還有commit scn,checkpoint scn,select scn等等)。
SQL> select dbms_flashback.get_system_change_number "system's scn" from dual;system's scn------------ 555956
oracle內部只有一個SCN,其他的都是來自它。我們還可以看一下資料庫裡面最小的SCN。
SQL> select creation_change# "oracle內部最小scn" from v$datafile where file#=1;oracle內部最小scn----------------- 9
我們加在oracle身上的事,無論好壞,oracle都會依據SCN,一一記在心裡(日誌),莫敢相忘。由於SCN是遞增的,我們對應到相關的SCN,就能找到那個時刻,我們對oracle所做的事。這便是SCN的重要性。
我們對oracle所在的事,她會記在當前日誌組。我們可以用v$log來查詢。
SQL> select group#,sequence#,status from v$log; GROUP# SEQUENCE# STATUS---------- ---------- ---------------- 1 5 CURRENT 2 3 INACTIVE 3 4 INACTIVE
接下來,我們對oracle做件事。我們建個表t,有兩個欄位。其中,欄位scn可以約等於事務開始的scn。
SQL> create table t(id int,scn number) tablespace users;Table created.SQL> insert into t values(1,dbms_flashback.get_system_change_number);1 row created.SQL> commit;Commit complete.SQL> select * from t; ID SCN---------- ---------- 1 585887
我們先把這件事緩緩,來看看v$log裡面的first_change#。
SQL> alter session set nls_date_format='yyyy/mm/dd hh24:mi:ss';Session altered.SQL> select group#,status,first_change#,first_time from v$log; GROUP# STATUS FIRST_CHANGE# FIRST_TIME---------- ---------------- ------------- ------------------- 1 CURRENT 583374 2012/07/17 19:59:23 2 INACTIVE 560959 2012/07/17 17:13:32 3 INACTIVE 560981 2012/07/17 17:14:33
這裡的first_change#和first_time是一樣的,都是SCN的兩種表現形式。first_change#是日誌組成為當前日誌組時所取的系統的SCN,來作為這一組最小或者開始的SCN。我們所做的事,對應的SCN,都會比first_change#來得大。
繼續我們的事,我們把當前日誌組歸檔。
SQL> alter system switch logfile;System altered.
再瞧瞧v$log裡面的first_change#
SQL> select group#,status,first_change#,first_time from v$log; GROUP# STATUS FIRST_CHANGE# FIRST_TIME---------- ---------------- ------------- ------------------- 1 ACTIVE 583374 2012/07/17 19:59:23 2 CURRENT 586090 2012/07/18 09:35:40 3 INACTIVE 560981 2012/07/17 17:14:33
現在當前日誌組變成了第2組,first_change#也發生了變化。
再來繼續我們未完的事。
SQL> insert into t values(2,dbms_flashback.get_system_change_number);1 row created.SQL> commit;Commit complete.SQL> select * from t; ID SCN---------- ---------- 1 585887 2 586129
從這裡我們可以看出,586129比當前日誌組2的first_change#(586090)大。從而,證明了first_change#是當前日誌組最小的SCN,之後,我們所做的任何事,產生的SCN,都會比這個來得大。
我們再日誌卻,將日誌組2歸檔。
SQL> alter system switch logfile;System altered.SQL> select group#,status,first_change#,first_time from v$log; GROUP# STATUS FIRST_CHANGE# FIRST_TIME---------- ---------------- ------------- ------------------- 1 ACTIVE 583374 2012/07/17 19:59:23 2 ACTIVE 586090 2012/07/18 09:35:40 3 CURRENT 586181 2012/07/18 09:39:21
現在,日誌組3變成了當前日誌組了,相應的first_change#也發生了變化。
再來繼續我們事情。為了產生更多的歸檔日誌,我們不斷的插入,提交,卻換。
SQL> insert into t values(3,dbms_flashback.get_system_change_number);1 row created.SQL> commit;Commit complete.SQL> alter system switch logfile;System altered.SQL> insert into t values (4,dbms_flashback.get_system_change_number);1 row created.SQL> commit;Commit complete.SQL> alter system switch logfile;System altered.SQL> insert into t values (5,dbms_flashback.get_system_change_number);1 row created.SQL> commit; Commit complete.SQL> alter system switch logfile;System altered.SQL> insert into t values(6,dbms_flashback.get_system_change_number);1 row created.SQL> commit;Commit complete.SQL> alter system switch logfile;System altered.SQL> insert into t values (7,dbms_flashback.get_system_change_number);1 row created.SQL> commit;Commit complete.SQL> alter system switch logfile;System altered.SQL> insert into t values (8,dbms_flashback.get_system_change_number);1 row created.SQL> commit;Commit complete.SQL> alter system switch logfile;System altered.SQL> select * from t; ID SCN---------- ---------- 1 585887 2 586129 3 586643 4 586666 5 586692 6 586722 7 586751 8 586805
我們再來看一下,當前日誌組是哪一組?
SQL> select group#,status,first_change#,first_time from v$log; GROUP# STATUS FIRST_CHANGE# FIRST_TIME---------- ---------------- ------------- ------------------- 1 ACTIVE 586734 2012/07/18 09:45:12 2 ACTIVE 586762 2012/07/18 09:46:15 3 CURRENT 586816 2012/07/18 09:47:15
當前的日誌組是3.那麼,我們再來插入。
SQL> insert into t values(9,dbms_flashback.get_system_change_number);1 row created.SQL> commit;Commit complete.
這時,我們並沒有卻換日誌組。然後,再插入。
SQL> insert into t values(10,dbms_flashback.get_system_change_number);1 row created.
注意了,此時,我們沒有提交也沒有卻換。那麼,第9,10條的資料都在日誌組3上面。
這裡,我們類比一個實驗來闡述備份與恢複的基本原理。
實驗:順利關機下,資料檔案損壞的完全恢複。
[oracle@localhost ~]$ sqlplus /nologSQL*Plus: Release 10.2.0.1.0 - Production on Tue Jul 17 20:48:19 2012Copyright (c) 1982, 2005, Oracle. All rights reserved.SQL> conn / as sysdbaConnected.SQL> shutdown immediateDatabase closed.Database dismounted.ORACLE instance shut down.[oracle@localhost ORCL]$ cd datafile/[oracle@localhost datafile]$ lso1_mf_example_8050jhm7_.dbf o1_mf_temp_8050j34j_.tmpo1_mf_sysaux_8050fk3w_.dbf o1_mf_undotbs1_8050fkc6_.dbfo1_mf_system_8050fk2z_.dbf o1_mf_users_8050fkdh_.dbf[oracle@localhost datafile]$ rm o1_mf_system_8050fk2z_.dbf[oracle@localhost datafile]$ rm o1_mf_sysaux_8050fk3w_.dbf[oracle@localhost datafile]$ rm o1_mf_users_8050fkdh_.dbf [oracle@localhost datafile]$ rm o1_mf_undotbs1_8050fkc6_.dbf
這個時候,假如我們要啟動資料庫會報什麼錯呢?
[oracle@localhost ~]$ sqlplus /nologSQL*Plus: Release 10.2.0.1.0 - Production on Tue Jul 17 20:57:00 2012Copyright (c) 1982, 2005, Oracle. All rights reserved.SQL> conn / as sysdbaConnected to an idle instance.SQL> startupORACLE instance started.Total System Global Area 419430400 bytesFixed Size 1219760 bytesVariable Size 142607184 bytesDatabase Buffers 272629760 bytesRedo Buffers 2973696 bytesDatabase mounted.ORA-01157: cannot identify/lock data file 1 - see DBWR trace fileORA-01110: data file 1:'/u01/app/oracle/oradata/ORCL/datafile/o1_mf_system_8050fk2z_.dbf'
因為,有控制檔案,所以,我們會到mount狀態,這是個oracle的介態。這個狀態,我們可以做很多事。這時,它報檔案1不能鎖定。那麼,我們一個個來。先把冷備的檔案1拷來。
[oracle@localhost datafile]$ cp o1_mf_system_8050fk2z_.dbf /u01/app/oracle/oradata/ORCL/datafile
然後,再來開啟資料庫,看會報什麼錯?
SQL> alter database open;alter database open*ERROR at line 1:ORA-01113: file 1 needs media recoveryORA-01110: data file 1:'/u01/app/oracle/oradata/ORCL/datafile/o1_mf_system_8050fk2z_.dbf'
這個時候報的錯誤不一樣了。報檔案1需要媒介恢複。oracle是根據什麼報這個錯誤的呢?要瞭解這個,我們需要藉助兩個視圖。
SQL> select file#,checkpoint_change# from v$datafile; FILE# CHECKPOINT_CHANGE#---------- ------------------ 1 587004 2 587004 3 587004 4 587004 5 587004SQL> select file#,checkpoint_change# from v$datafile_header; FILE# CHECKPOINT_CHANGE#---------- ------------------ 1 583375 2 0 3 0 4 0 5 587004
這兩個視圖所取的資訊來源完全不一樣。v$datafile的資訊來自控制檔案;v$datafile_header的資訊則來自每個資料檔案的檔案頭。我們剛剛已經把file 1拷回來,所以,oracle可以讀到它頭上的scn。而2,3,4已經被刪了便是讀不到的。但是,file 1在兩處的scn不一致。記住了,oracle會橫向比較,縱向是不會比較的。即:不會拿file 1和file 3比較。oracle開啟的必要條件是控制檔案和資料檔案的檔案頭的scn要一致。那麼大於583375,而小於585469的scn都在歸檔日誌裡面。每個scn對應相關的操作。
SQL> select sequence#,first_change#,next_change# from v$archived_log; SEQUENCE# FIRST_CHANGE# NEXT_CHANGE#---------- ------------- ------------ 5 544404 558719 6 558719 559931 7 559931 560709 8 560709 560959 9 560959 560981 10 560981 583374
什麼是next_change#?日誌組由當前日誌組卻換到非當前日誌組時,所取的系統scn,來作為它的最大scn。first_change#是它成為current的開始;而next_change#則是它結束了current生涯的標誌。
我們知道,比583375小的scn都已經寫入資料檔案。現在,我們需要確定583375是落在哪對first_change#和next_change#之間。從而確定廣義前滾的起點。
SQL> select sequence#,first_change#,next_change# from v$archived_log 2 where 583375>=first_change# and 3 583375<=next_change#; SEQUENCE# FIRST_CHANGE# NEXT_CHANGE#---------- ------------- ------------ 11 583374 586090
由此,我們知道,583375落在歸檔日誌11的first_change#和next_change#之間。我們恢複的時候,就從歸檔日誌11開始。那麼,我們到底需要多少的歸檔日誌呢?
SQL> select sequence#,first_change#,next_change# from v$archived_log 2 where sequence#>=11; SEQUENCE# FIRST_CHANGE# NEXT_CHANGE#---------- ------------- ------------ 11 583374 586090 12 586090 586181 13 586181 586656 14 586656 586676 15 586676 586704 16 586704 586734 17 586734 586762 18 586762 586816
從上面可知,如果我們想把資料全部找回,我們需要藉助到歸檔日誌18.我們看一下這些first_change#和next_change#有什麼特色?
歸檔日誌11的next_change#是歸檔日誌12的first_change#。以此類推,所以,這麼多的歸檔日誌,其實,邏輯上就只是一個歸檔日誌。因此,歸檔日誌必須連續!如果,你歸檔日誌13壞了,那麼只能恢複到12的next_change#。後面再多的歸檔也是徒然。
接下來,我們開始恢複。
SQL> recover datafile 1;ORA-00279: change 583375 generated at 07/17/2012 19:59:23 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_11_%u_.arcORA-00280: change 583375 for thread 1 is in sequence #11Specify log: {<RET>=suggested | filename | AUTO | CANCEL}
oracle告訴我們,583375 對於執行個體是需要的。並且,歸檔日誌11在閃回區。如果敲斷行符號,則採納oracle的建議,oracle會自己到閃回區裡面去找。我們敲一下斷行符號鍵採納oracle的建議。第二個選項,是不在預設路徑裡面,由你來告訴oracle,歸檔日誌身在何處。你只要告訴oracle,歸檔日誌的絕對路徑+名稱,就可以了。第三個選項,如果歸檔日誌很多,一個個挨著去找,顯得很麻煩,那麼我們就去auto。第四個選項,如果恢複到一半,或者,沒有了歸檔日誌,那麼你可以敲cancel。
ORA-00279: change 586090 generated at 07/18/2012 09:35:40 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_12_%u_.arcORA-00280: change 586090 for thread 1 is in sequence #12ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_11_80d4qdmh_.arc' no longer needed for this recoverySpecify log: {<RET>=suggested | filename | AUTO | CANCEL}
這時,oracle會再告訴我們,日誌12是執行個體所需要的。我們先把這事給擱著。先去資料檔案檔案頭,把scn給取出來瞧瞧。
SQL> select file#,checkpoint_change# from v$datafile_header; FILE# CHECKPOINT_CHANGE#---------- ------------------ 1 586090 2 0 3 0 4 0 5 587004
發現沒?file 1 的scn變成了586090。而586090是歸檔日誌12的first_change#。難怪oracle告訴我們日誌12是執行個體必須的。接下來,我們敲auto。
Specify log: {<RET>=suggested | filename | AUTO | CANCEL}autoORA-00279: change 586181 generated at 07/18/2012 09:39:21 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_13_%u_.arcORA-00280: change 586181 for thread 1 is in sequence #13ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_12_80d4y9fp_.arc' no longer needed for this recoveryORA-00279: change 586656 generated at 07/18/2012 09:42:06 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_14_%u_.arcORA-00280: change 586656 for thread 1 is in sequence #14ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_13_80d53g42_.arc' no longer needed for this recoveryORA-00279: change 586676 generated at 07/18/2012 09:42:59 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_15_%u_.arcORA-00280: change 586676 for thread 1 is in sequence #15ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_14_80d5541h_.arc' no longer needed for this recoveryORA-00279: change 586704 generated at 07/18/2012 09:44:03 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_16_%u_.arcORA-00280: change 586704 for thread 1 is in sequence #16ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_15_80d57315_.arc' no longer needed for this recoveryLog applied.Media recovery complete.
發現沒?oracle只運用了歸檔日誌到16。接下來的17,18就沒有再提示了。為什嗎?我們先去資料檔案的檔案頭把file 1的scn再取來看看。
SQL> select file#,checkpoint_change# from v$datafile_header; FILE# CHECKPOINT_CHANGE#---------- ------------------ 1 587003 2 0 3 0 4 0 5 587004
587003是不是比歸檔日誌18的next_change#(586816)來得大呢。我們再來看看,當前日子組是哪一組。
SQL> select group#,sequence#,status,first_change# from v$log; GROUP# SEQUENCE# STATUS FIRST_CHANGE#---------- ---------- ---------------- ------------- 1 17 INACTIVE 586734 3 19 CURRENT 586816 2 18 INACTIVE 586762
可以看出,歸檔日誌19的first_change#為586816。而資料檔案頭的scn是587003。當我們敲recover datafile 1時,oracle在做完全恢複。完全恢複的起點和終點是已經確定了。起點在資料檔案的檔案頭,終點在控制檔案裡擷取。因為,歸檔重做記錄檔17,18是從聯機重做記錄檔1,2裡面讀出來的。oracle會優先去找聯機重做記錄檔。或者說,完全恢複時,oracle會自己去找聯機重做記錄檔;不完全恢複,我們可以把online redo log file的絕對路徑和名稱輸進去。當前日誌組是3,它的first_change#為586816,而587003比這個數大。可見,oracle也將當前記錄檔給用上了。
接著恢複。這次,我們把剩餘的資料檔案全部拷回。然後大家一起往前走,直到步伐一致時,才能夠同時停下來,這樣子,oracle就處於一致的狀態了。
SQL> recover database;ORA-00279: change 583375 generated at 07/17/2012 19:59:23 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_11_%u_.arcORA-00280: change 583375 for thread 1 is in sequence #11Specify log: {<RET>=suggested | filename | AUTO | CANCEL}autoORA-00279: change 586090 generated at 07/18/2012 09:35:40 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_12_%u_.arcORA-00280: change 586090 for thread 1 is in sequence #12ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_11_80d4qdmh_.arc' no longer needed for this recoveryORA-00279: change 586181 generated at 07/18/2012 09:39:21 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_13_%u_.arcORA-00280: change 586181 for thread 1 is in sequence #13ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_12_80d4y9fp_.arc' no longer needed for this recoveryORA-00279: change 586656 generated at 07/18/2012 09:42:06 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_14_%u_.arcORA-00280: change 586656 for thread 1 is in sequence #14ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_13_80d53g42_.arc' no longer needed for this recoveryORA-00279: change 586676 generated at 07/18/2012 09:42:59 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_15_%u_.arcORA-00280: change 586676 for thread 1 is in sequence #15ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_14_80d5541h_.arc' no longer needed for this recoveryORA-00279: change 586704 generated at 07/18/2012 09:44:03 needed for thread 1ORA-00289: suggestion :/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_16_%u_.arcORA-00280: change 586704 for thread 1 is in sequence #16ORA-00278: log file'/u01/app/oracle/flash_recovery_area/ORCL/archivelog/2012_07_18/o1_mf_1_15_80d57315_.arc' no longer needed for this recoveryLog applied.Media recovery complete.
接下來,我們就可以開啟資料庫了。
SQL> alter database open;Database altered.
事務對應的scn如果落在了哪個archivelog裡,那麼這個archivelog在恢複時就被用到 .
總結,這篇部落格裡,我利用SCN和順利關機下資料檔案損壞的完全恢複來協助自己和大家一起理解oracle備份與恢複的原理。如果有足,希望走過路過的網友,給力批評。oracle的備份與恢複是門藝術。大家一起成長。go for it。