ORACLE事務和例項恢復過程梳理

darren__chan發表於2019-07-29
最近與一個前輩討論起關於oracle例項恢復的問題,其實以前都研究過,無奈近一年沒怎麼研究原理性東西,基本大部分都忘光了,所以場面極度尷尬,現在只能好好再來梳理實驗一把,並且把mysql的例項恢復也梳理一下。

從oracle 的一個事務說起:
例如當我們發起一個update 語句更改某一行資料,例如更改zabbix.cwdtest 的 table_name='ICOL$' 為'aaxx'。該行記錄在14號檔案,1835491塊


1.首先會經過在share pool中的sql語句的解析過程,這一過程只要是針對sql語法,執行計劃這些進行處理,這一部分不細講。

2.接著,到了sql執行後,資料庫從物理檔案讀出資料行相應的資料庫到 buffer cache中(假設此時記憶體不存在相應的資料塊同時不討論鎖的過程),這一過程也涉及到資料塊寫到dirty list,並寫髒塊,為新讀取的資料塊尋找空閒空間的過程


3.同時會分配回滾段並在undo段再保留一份修改前的資料塊映像

以下通過DUMP UNDO 相關 資訊來檢視。


看到index是0x09的事務槽的 state 為 10代表事務正在活動,而其他槽是 9代表事務不活動,

scn   表示務事啟動、提交、回滾的SCN,事務槽 0x09的scn是 0x0009.01e25a30,轉換之後是38686317104。

dba   表示uba:第一部分的undo塊地址,這個DBA是(rollback)回滾的起始點,也就是說是記錄事務修改的最後一條記錄所在UNDO塊的地址。

事物表中0x19槽的dba為0x0a400495即41號檔案的1173號塊塊號這與(與v$transaction檢視中一致)。


我們在看一下這個前映象到底是什麼?

轉儲資料塊

這裡scn表示 最近寫入磁碟的SCN號,此時資料塊中的scn是 0x0009.01e25beb,轉化之後是38686317547。
對於DBWR,每次重新整理髒塊後,會去維護這個block的SCN號,代表這個block的資料版本。

接著往下看,有本UNDO塊中有51條記錄:


我們找到第51行記錄的物件號是130736,這個正是我們本次事務更改的表。


這裡欄位值是 49 43 4f 4c 24,通過轉換是ICOL$,這正是update前的值。

SQL> select utl_raw.cast_to_varchar2(replace('49 43 4f 4c 24',' ')) from dual;
UTL_RAW.CAST_TO_VARCHAR2(REPLACE('49434F4C24',''))
-------------------------------------------------------------------------------------
ICOL$
而bdba: 0x039c01e3 是該記錄對應的資料塊地址,該行記錄正是在14號檔案,1835491塊
SQL> select to_number('039c01e3','xxxxxxxxxxx')from dual;
TO_NUMBER('039C01E3','XXXXXXXXXXX')
-----------------------------------
               60555747
SQL> select dbms_utility.data_block_address_file(60555747)file#, dbms_utility.data_block_address_block(60555747) block  from dual;
    FILE#          BLOCK
------------- -------------
       14        1835491

 









整個與UNDO相關的有幾個SCN,我們重新整理下:
undo header 中的SCN:  表示務事啟動、提交、回滾的SCN,事務槽 0x09的scn是 0x0009.01e25a30,轉換之後是38686317104。
undo block中scn: 表示 最近寫入磁碟的SCN號, 此時資料塊中的scn是 0x0009.01e25beb,轉化之後是38686317547
undo block修改記錄行的SCN:
ctl max scn: 0x0009.01e25b73 轉化之後是38686317427
prv tx scn: 0x0009.01e25b75 ,轉化之後是38686317429
txn start scn: scn: 0x0000.00000000 ,這裡是0 。

而此時redo記錄的資訊如下:
alter system dump logfile '/opt/app/oracle/oradata/tlvdb/redo1.log';


4..為事務修改資料塊,並 在執行更改完成後, 針對此資料塊的修改也生成redo資訊。這裡是將該語句所影響的並被讀入db buffer中的這些行資料的rowid及要更新的原值和新值及scn等資訊從PGA逐條的寫入redo log buffer中,

以下分別UPDATE 前後執行alter system dump datafile 14 block 1835491;來dump 出資料塊。 
執行UPDATE之前:
tab 0, row 0, @0x1e87
tl: 249 fb: --H-FL-- lb: 0x0 cc: 55
col 0: [ 3] 53 59 53
col 1: [ 5] 49 43 4f 4c 24<<<<<< 轉換成字元後是'ICOL$'
Block dump from disk:
buffer tsn: 7 rdba: 0x039c01e3 (14/1835491)
scn: 0x0009.01e25a2b seq: 0x02 flg: 0x04 tail: 0x5a2b0602
frmt: 0x02 chkval: 0xf28b type: 0x06=trans data
Hex dump of block: st=0, typ_found=1


這裡scn: 0x0009.01e25a2b 轉換之後是38686317099

執行UPDATE 之後:
tl: 248 fb: --H-FL-- lb: 0x2 cc: 55
col 0: [ 3] 53 59 53
col 1: [ 4] 61 61 78 78<<<<<<<<< 轉換成字元後是'aaxx'
Block dump from disk:
buffer tsn: 7 rdba: 0x039c01e3 (14/1835491)
scn: 0x0009.01e25beb seq: 0x01 flg: 0x04 tail: 0x5beb0601
frmt: 0x02 chkval: 0xad91 type: 0x06=trans data
Hex dump of block: st=0, typ_found=1
這裡scn: 0x0009.01e25beb 轉換之後是38686317547

這裡說明,當事務中修改了資料塊,不管事務有無提交,資料髒塊會隨著 dbw程式的機制寫入資料檔案中。 
而在 REDO日誌中會記錄操作的記錄,並 記錄本次undo操作的資訊,即修改前的資訊:
REDO RECORD - Thread:1 RBA: 0x000412.00335b66.0010 LEN: 0x01a0 VLD: 0x0d
SCN: 0x0009.01e25beb SUBSCN:  1 07/22/2019 17:53:43
(LWN RBA: 0x000412.00335b66.0010 LEN: 0001 NST: 0001 SCN: 0x0009.01e25beb)
CHANGE #1 TYP:0 CLS:1 AFN:14 DBA:0x039c01e3 OBJ:130736 SCN:0x0009.01e25a2b SEQ:2 OP:11.5 ENC:0 RBL:0
KTB Redo 
op: 0x01  ver: 0x01  
compat bit: 4 (post-11) padding: 1
op: F  xid:  0x000b.009.0001ba87    uba: 0x0a400495.639a.51
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x039c01e3  hdba: 0x039c01e2
itli: 2  ispac: 0  maxfr: 4858
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 2 ckix: 0
ncol: 55 nnew: 1 size: -1
col  1: [ 4]  61 61 78 78
CHANGE #2 TYP:0 CLS:37 AFN:3 DBA:0x00c00170 OBJ:4294967295 SCN:0x0009.01e25bb3 SEQ:1 OP:5.2 ENC:0 RBL:0
ktudh redo: slt: 0x0009 sqn: 0x0001ba87 flg: 0x0012 siz: 140 fbi: 0
            uba: 0x0a400495.639a.51    pxid:  0x0000.000.00000000
CHANGE #3 TYP:0 CLS:38 AFN:41 DBA:0x0a400495 OBJ:4294967295 SCN:0x0009.01e25bb2 SEQ:1 OP:5.1 ENC:0 RBL:0
ktudb redo: siz: 140 spc: 646 flg: 0x0012 seq: 0x639a rec: 0x51
            xid:  0x000b.009.0001ba87  
ktubl redo: slt: 9 rci: 0 opc: 11.1 [objn: 130736 objd: 130736 tsn: 7]
Undo type:  Regular undo        Begin trans    Last buffer split:  No 
Temp Object:  No 
Tablespace Undo:  No 
             0x00000000  prev ctl uba: 0x0a400495.639a.50 
prev ctl max cmt scn:  0x0009.01e25b73  prev tx cmt scn:  0x0009.01e25b75 
txn start scn:  0x0000.00000000  logon user: 0  prev brb: 171967629  prev bcl: 0 BuExt idx: 0 flg2: 0
KDO undo record:
KTB Redo 
op: 0x03  ver: 0x01  
compat bit: 4 (post-11) padding: 1
op: Z
KDO Op code: URP row dependencies Disabled
  xtype: XA flags: 0x00000000  bdba: 0x039c01e3  hdba: 0x039c01e2
itli: 2  ispac: 0  maxfr: 4858
tabn: 0 slot: 0(0x0) flag: 0x2c lock: 0 ckix: 0
ncol: 55 nnew: 1 size: 1
col  1: [ 5]  49 43 4f 4c 24 《〈〈更改前映象資料
5.執行commit命令,宣告redo log buffer 中的redo處於commit狀態,然後修改事務表相應slot,宣告事務已提交,最後通知lgwr程式將redo log buffer中的資料寫入redo log file。待lgwr程式通知已經寫入完成後,向使用者發出commit完成的資訊。


在事務的過程中涉及到三個程式,CKPT,DBWn,LGWR程式 :
CKPT:檢查點程式(Checkpoint Process)只是更新資料檔案的檔案首部,以輔助建立檢查點的程式
ckpt 觸發條件:

什麼時候發生normal checkpoint

下面這些操作將會觸發checkpoint事件:

·           日誌切換,通過ALTER SYSTEM SWITCH LOGFILE。

·           DBA 發出checkpoint命令,通過ALTER SYSTEM checkpoint。

·           對資料檔案進行熱備時,針對該資料檔案的checkpoint也會進行,ALTER TABLESPACE TS_NAME BEGIN BACKUP/END BACKUP。

·           當執行ALTER TABLESPACE/DATAFILE READ ONLY的時候。

·           SHUTDOWN 命令發出時。

因為每次完全的checkpoint都需要把buffer cache所有的髒塊都寫入到資料檔案中,這樣就是產生一個很大的IO消耗,頻繁的完全checkpoint操作很對系統的效能有很大的影響,為此Oracle引入的增量checkpoint的概念,buffer cache中的髒塊將會按照BCQ佇列的順序持續不斷的被寫入到磁碟當中,同時CKPT程式將會每3秒中檢查DBWn的寫入進度並將相應的RBA資訊記錄到控制檔案中。

有了增量checkpoint之後在進行例項恢復的時候就不需要再從崩潰前的那個完全checkpoint開始應用重做日誌了,只需要從控制檔案中記錄的RBA開始進行恢復操作,這樣能節省恢復的時間。

發生增量checkpoint的先決條件

·           恢復需求設定 (FAST_START_IO_TARGET/FAST_START_MTTR_TARGET)

·           LOG_checkpoint_INTERVAL 引數值

·           LOG_checkpoint_TIMEOUT 引數值

·           最小的日誌檔案大小

·           buffer cache 中的髒塊的數量

 

DBWn:資料庫塊寫入器(Database Block Writer)負責將髒塊寫入磁碟的後臺程式。 
觸發DBWR程式的條件有: 
1.DBWR超時,大約3秒 
2.系統中沒有多餘的空緩衝區來存放資料 
3.CKPT 程式觸發DBWR
4.free buffer waits 40% 觸發 dbwr吧lruw髒塊寫入磁碟
5.關機會觸發dbwr寫
6.alter system checkpoint 觸發dbwr寫
7.redo 日誌切換 觸發dbwr寫
8. 表空間 offline /online 觸發dbwr寫

LGWR:日誌寫入器(Log Writer)負責將SGA中重做日誌緩衝區的內容重新整理輸出到磁碟。 
           觸發LGWn工作的時點有幾個:
  1. 使用者提交 ,使用者提交時必須寫LOGFILE.
  2. 有1/3重做日誌緩衝區未被寫入磁碟 
  3. 有大於1M的重做日誌緩衝區未被寫入磁碟 
  4. 3秒超時 
  5. DBWR 需要寫入的資料的SCN大於LGWR記錄的SCN,DBWR 觸發LGWR寫入。 

這三個程式都是為了更好地完成一件事:安全高效地實現記憶體資料塊寫入資料檔案,就是將記憶體中修改的資料反映到硬碟的資料檔案上。 我在事務未提交時做一個重新整理buffer cache,此時發現資料塊已經被重新整理到磁碟檔案中,通過以上三個程式的觸發條件也可以看出重新整理資料塊與事務是否提交關係不大。

首先,日誌檔案的寫入是很頻繁的。LGWn會不斷將日誌資訊從Log Buffer中寫入Online Redo Log;
其次,在日誌檔案上,可以有三個型別的事務事件。
1、事務結束,已經被commit,之後打過checkpoint檢查點。
2、事務結束,已經被commit,之後沒有打入checkpint檢查點。
3、事務未結束,沒有commit。
那麼當我有一個事務一直未提交,此時發生斷電,資料庫直接crash,在重啟後ORACLE是如何保證資料一致性呢?

啟動資料庫時,如果發現有datafile header的START SCN 不等於儲存於CONTROLFILE的DATAFILE SCN,表示需要進行介質恢復

啟動資料庫時,如果發現STOP SCN = NULL,表示需要進行crash recovery。

oracle 的例項恢復過程

例項恢復主要經歷三個階段 : cache recovery、open database、transaction recovery

1.首先oracle對比控制檔案中檢查點scn與資料檔案頭部scn,發現不一致
但我開啟一個事務,並不作提交時,首先來看控制檔案中記錄的 SCN情況:
最近一次完全檢查點checkpoint change scn:
DATABASE ENTRY
***************************************************************************
。。。。
 Controlfile Creation Timestamp  08/21/2017 11:01:49
 Incmplt recovery scn: 0x0000.00000000
 Resetlogs scn: 0x0000.000e2006 Resetlogs Timestamp  08/21/2017 11:01:51
 Prior resetlogs scn: 0x0000.00000001 Prior resetlogs Timestamp  08/24/2013 11:37:30
 Redo Version: compatible=0xb200400
 #Data files = 53, #Online files = 53
 Database checkpoint: Thread=1 scn: 0x0009.01e35ecd
 Threads: #Enabled=1, #Open=1, Head=1, Tail=1
這裡是 Database checkpoint: Thread=1 scn: 0x0009.01e35ecd,轉換之後是38686383821。

而增量檢查點SCN
CHECKPOINT PROGRESS RECORDS
***************************************************************************
 (size = 8180, compat size = 8180, section max = 11, section in-use = 0,
  last-recid= 0, old-recno = 0, last-recno = 0)
 (extent = 1, blkno = 2, numrecs = 11)
THREAD #1 - status:0x2 flags:0x0 dirty:15
low cache rba:(0x413.18481.0)〉〉〉起點  on disk rba:(0x413.1849b.0)〉〉〉終點
on disk scn: 0x0009.01e374ef 07/24/2019 10:23:54 
resetlogs scn: 0x0000.000e2006 08/21/2017 11:01:51
heartbeat: 996528373 mount id: 1186014334
low cache rba就是CKPT記錄的DBWR寫髒塊的進度是最近一次完全checkpoint scn 的位置, on disk rba就是LGWR的寫進度, 是 lgwr 寫日誌檔案的最末位置的地址
low cache rba 以前的更新的髒塊已經寫入資料檔案,不需要重做, on disk rba以後的日誌還沒寫入到online logfile.所以不需要恢復.
而此時有15個髒塊。 
on disk scn表示當前系統最新的rba對應的scn,由CKPT程式每3秒跟新一次。如果資料庫異常當機,那麼CRASH RECOVER時伺服器至少要應用到該SCN為止。
這裡的增量檢查點SCN是38686389487,這個scn會比全量檢查點後的SCN大。

控制檔案中記錄的資料檔案 SCN:
DATA FILE #14: 
  name #18: /opt/app/oracle/oradata/xxxx/xxx
creation size=2097152 block size=8192 status=0xe head=18 tail=18 dup=1
 tablespace 7, index=7 krfil=14 prev_file=13
 unrecoverable scn: 0x0000.00000000 01/01/1988 00:00:00
 Checkpoint cnt:1061 scn: 0x0009.01e35ecd 07/24/2019 07:30:51
 Stop scn: 0xffff.ffffffff 09/27/2018 09:20:13
 Creation Checkpointed at scn:  0x0000.000f4212 08/21/2017 12:03:57
 thread:1 rba:(0x8.3f45.10)

 

這裡記錄的資料檔案的scn 也同樣是 38686383821,而Stop scn是無窮大,表示當前資料庫是正常開啟狀態,或者異常關閉狀態。
再看資料檔案頭中SCN:
oradebug dump file_hdrs 1
DATA FILE #14: 
  name #18: /opt/app/oracle/oradata/xxxx/xxx
creation size=2097152 block size=8192 status=0xe head=18 tail=18 dup=1
 tablespace 7, index=7 krfil=14 prev_file=13
 unrecoverable scn: 0x0000.00000000 01/01/1988 00:00:00
 Checkpoint cnt:1061 scn: 0x0009.01e35ecd 07/24/2019 07:30:51
這裡記錄的資料檔案頭SCN 與控制檔案中記錄的一致。 

以上這種情況當發現控制檔案中資料檔案的SCN與資料檔案頭中記錄 的SCN一致,即不會發生介質恢復。
當啟動時發現 Stop scn 是無窮大,則表示資料庫是異常關閉了,需要進行 crash recovery,即回滾。而其中增量檢查點的作用就是記錄了該從哪個地方回滾到那個地方。
此時,我們 模式 資料庫crash ,然後重啟:
alter database open
Beginning crash recovery of 1 threads
 parallel recovery started with 32 processes
Started redo scan
Completed redo scan
 read 16 KB redo, 14 data blocks need recovery
Started redo application at
 Thread 1: logseq 1043, block 185196
Recovery of Online Redo Log: Thread 1 Group 2 Seq 1043 Reading mem 0
  Mem# 0: /opt/app/oracle/oradata/tlvdb/redo2.log
Completed redo application of 0.01MB
Completed crash recovery at
 Thread 1: logseq 1043, block 185228, scn 38686432573《〈〈〈〈〈〈〈〈
 14 data blocks read, 14 data blocks written, 16 redo k-bytes read
Wed Jul 24 23:44:50 2019
Thread 1 advanced to log sequence 1044 (thread open)
Thread 1 opened at log sequence 1044
  Current log# 3 seq# 1044 mem# 0: /opt/app/oracle/oradata/tlvdb/redo3.log
Successful open of redo thread 1
MTTR advisory is disabled because FAST_START_MTTR_TARGET is not set
Wed Jul 24 23:44:50 2019
SMON: enabling cache recovery
[39962] Successfully onlined Undo Tablespace 2.
Undo initialization finished serial:0 start:193329648 end:193329738 diff:90 (0 seconds)
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
2.從最後檢查點之後到日誌檔案尾部將被重新應用到資料檔案,同時產生undo資訊(回滾),此階段也稱為cache recovery
當資料庫中有已經COMMIT但沒有寫入磁碟的資料塊,當資料庫CRASH後重啟,會從控制檔案中最近一次完全檢查點位置到最後一次增量檢查點位置,例如上面從 38686383821到 38686389487。
然後到redo日誌檔案中找到該檢查點位置,然後從該檢查點位置開始往下到增量檢查點位置,應用所有的redo,而其尋找redo條目是從low cache rba到on disk rba,從而在buffer cache裡又恢復了例項崩潰那個時間點的狀態。這個過程叫做前滾,前滾完畢以後,
buffer cache裡既有崩潰時已經提交還沒有寫入資料檔案的髒資料塊,也還有事務被突然終止,而導致的既沒有提交又沒有回滾的事務所弄髒的資料塊。
可以把這個過程的redo給dump出來:
SQL> ALTER SYSTEM DUMP LOGFILE '/opt/app/oracle/oradata/tlvdb/redo1.log'  SCN MIN 38686383821  SCN MAX 38686389487;
System altered.
 Opcodes *.*
 RBAs: 0x000000.00000000.0000 thru 0xffffffff.ffffffff.ffff
 SCNs: scn: 0x0009.01e35ecd (38686383821) thru scn: 0x0009.01e374ef (38686389487)
 Times: creation thru eternity
 FILE HEADER:
。。。。
        Control Seq=459718=0x703c6, File size=4194304=0x400000
        File Number=1, Blksiz=512, File Type=2 LOG
 descrip:"Thread 0001, Seq# 0000001042, SCN 0x000901d8f38d-0x000901e35ecd"
 thread: 1 nab: 0x384d91 seq: 0x00000412 hws: 0x3 eot: 0 dis: 0
 resetlogs count: 0x38c7849f scn: 0x0000.000e2006 (925702)
 prev resetlogs count: 0x3121c97a scn: 0x0000.00000001 (1)
 Low  scn: 0x0009.01d8f38d (38685701005) 07/08/2019 09:24:12
 Next scn: 0x0009.01e35ecd (38686383821) 07/24/2019 07:30:51
 Enabled scn: 0x0000.000e2006 (925702) 08/21/2017 11:01:51
 Thread closed scn: 0x0009.01d8f38d (38685701005) 07/08/2019 09:24:12
 Disk cksum: 0x56ca Calc cksum: 0x56ca
 Terminal recovery stop scn: 0x0000.00000000
 Terminal recovery  01/01/1988 00:00:00
 Most recent redo scn: 0x0000.00000000
 Largest LWN: 2395 blocks

3.資料檔案中包含已提交或未提交的資料,儘管存在未提交的資料,此時資料庫已經被開啟,允許使用者連線,此時針對 未提交的事務被進行回滾

前滾一旦完畢,SMON程式立即開啟資料庫。但是,這時的資料庫中還含有那些中間狀態的、既沒有提交又沒有回滾的髒塊,這種髒塊是不能存在於資料庫中的,因為它們並沒有被提交,必須被回滾。開啟資料庫以後,SMON程式會在後臺進行回滾,此時會從Undo空間中尋找到舊版本SCN的資料塊資訊,來進行SGA中Buffer Cache資料塊恢復。




幾個疑問:
一,當log buffer 中的資料還沒來得及寫入redo file,此時資料庫crash掉,那這部分丟失redo怎麼辦?
Oracle 採取在事務提交的時候將和這個事務相關的REDO LOG 資料,包括COMMIT 記錄,都必須從LOG BUFFER 中寫入REDO LOG 檔案,此時事務提交成功的訊號才能傳送給使用者程式。這樣便可以確保當已經提交的事務中的部分BUFFER CACHE 還沒有被寫入資料檔案時發生了例項故障,在重啟後做例項恢復的時候,也可以通過REDO LOG 的資訊,將不一致的資料前滾。
如果某事務未提交,此時產生的REDO仍在LOG  BUFFER中,當資料庫突然CRASH,log buffer資料也丟失了,我們可以確認這部分資料是沒有提交的,將會被回滾,但是,redo資訊沒有寫入磁碟,不存在了。也沒辦法通過redo 構造資料塊,怎麼辦?
其實這裡是理解錯了,未寫入redo_file,也未寫入db_file,那麼發生資料庫失敗恢復時,資料庫將直接丟棄該DML操作,反正該操作尚未commit,丟掉了也沒關係,並且因為未寫入資料檔案,沒必要進行構造UNDO回滾。 
 
二、UNDO有沒有寫快取情況,如果有UNDO丟失怎麼辦?
undo 有undo buffer ,從以上可以看出,redo 日誌會記錄了事務的 redo 和undo資訊。所以不用擔心

三、例項恢復過程主要使用的四個SCN:

Control file三個地方為:
1、System checkpoint SCN
select checkpoint_change# from v$database;
這裡應該有完整檢查點的SCN,完整檢查點的SCN會更新資料檔案及資料檔案頭的SCN, 增量檢查點SCN只在控制檔案中更新。
2、Datafile checkpoint SCN
set linesize 400
col name for a50
 select name, checkpoint_change# from v$datafile where file#=14;
3、 Stop SCN
select name,last_change# from v$datafile where file#=14;
正常datafile在read-write mode運作下,last_change#一定是null

還有一個SCN在datafile header內
4、 Start SCN
select name,checkpoint_change# from v$datafile_header where file#=14;


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29863023/viewspace-2652139/,如需轉載,請註明出處,否則將追究法律責任。

相關文章