Oracle 資料庫恢復操作最關鍵的依據就是 redo log
,它記錄了對資料庫所有的更改操作。在研究如何提取 redolog 中 DML 操作的過程可謂一波三折,因為介紹 redolog 結構細節的資料實在太少了,不過好在最後大致理清了它的結構,並開發了一個基於日誌的同步軟體。
本系列文章就記錄下研究過程中遇到的問題和使用的分析命令、工具。
1. 什麼是 Redo Log
Redo Log 就是一組檔案,它們記錄了對資料庫的所有操作,主要包括:
- 所有
DML
操作,INSERT\UPDATE\DELETE\SELECT FOR UPDATE
- 所有
DDL
操作,CREATE TABLE\ALTER TABLE
等 - 所有因
Recursive SQL
引起的變化,比如執行 DDL 語句時,Oracle 會隱式的執行其他 SQL 修改資料字典
在資料庫事務 COMMIT
之前,Oracle 既會把變化資訊寫入 Rodo Log 檔案,也會把原始資料-即 UNDO Segments
寫入。因此,Redo Log 不僅用於恢復資料,還能保護資料回滾。
2. Oracle 如何寫入 Redo Log
Oracle 資料庫每個例項都有一個 redo thread
負責日誌的寫入,稱為 LGWR
,LoG WRiter 的簡寫。Redo Log 檔案至少有兩個,LGWR 採用迴圈覆蓋的方式寫入:當一個檔案寫滿後,開始寫入下一個,當最後一個檔案寫滿後,返回第一個開始寫入,如此迴圈。
這樣寫入會導致資料丟失嗎?預設情況下會。
資料庫日誌有兩種模式:歸檔
和 非歸檔
,非歸檔模式就會有覆蓋寫入的問題。在歸檔模式下,當一組 ReodLog 檔案寫滿,發生切換時,Oracle 會保證歸檔完成前此組檔案不被覆蓋。
可以使用以下命令手動觸發日誌切換:
SQL> alter system switch logfile
3. Redo Log 基本結構
Redo Log 是由一系列的 redo record
組成,每個 redo record
又是由一組 change vector
組成,每個 change vector
都記錄了對單個資料塊的更改操作。
Redo Log 檔案在儲存結構上,是按塊儲存的,預設情況下塊大小是磁碟扇區的大小,通常是 512 位元組
,它的格式取決於作業系統和資料庫版本,這裡的分析都是在 Windows Server 2008 R2
和 Oracle 11g
的基礎上進行的。
Redo Log 是按順序寫入的,基本格式如下:
前兩個塊記錄的是元資訊,分別是:
- 第1個塊記錄檔案本身的資訊,比如檔案型別,塊大小和塊數,這部分稱為
File Header
- 檔案頭 - 第2個塊記錄資料庫例項相關資訊,比如資料庫SID,資料庫版本,這部分稱為
Redo Log Header
- 重做日誌頭
結合日誌寫入方式,從整體上看,讀取這一組 Redo Log 檔案,在記憶體中可以把它們看成按塊為儲存單元的環形緩衝區來處理,解析的過程就是讀取一個個 Record
。
3.1 Redo Record
一個 Redo Record 可能佔用一個 block,也可能佔用多個 block,也可能只佔用 block 的一部分,這取決於它的長度,長度欄位就儲存在 Record Header
頭部,結構如下:
值得注意的是,Record 的頭長度是動態的,計算方法以及二進位制檔案位元組分析後續文章會詳細介紹,這裡簡單看下使用 system dump
命令匯出的頭資訊:
REDO RECORD - Thread:1 RBA: 0x000009.0000029e.0010 LEN: 0x02ac VLD: 0x0d
SCN: 0x0000.0010c5e6 SUBSCN: 1 01/02/2021 21:09:41
其中主要欄位的含義:
- RBA: Redo Byte Address, 由三部分組成:日誌序號(0x9),塊編號(0x29e),塊中位元組偏移量(0x10)
- LEN: Record 長度,包含頭部長度
- VLD: 頭部長度標識,按照一定的邏輯計算頭的長度,
0x0d
就表示頭部長度為68 位元組
- SCN: System Change Number,也可稱為 System Commit Number。當一個事務提交時,LOWR 將緩衝區內容寫入檔案,併為每個已提交的事務,分配一個標識,就是 SCN。也就是說,可以通過 SCN 跟蹤資料庫變化,也可以根據它決定從哪開始恢復資料。
此外,藉助 SCN 還可以有針對的 dump 執行的 DML 語句,本文最後有相關的命令。
3.2 Change Vector
Record 頭後面就跟著,一個或多個 Change Vector
,每個 Change
都代表一個資料庫操作,比如增刪改,事務開始,事務回滾,事務提交等等,它的格式如下:
其中:
Change Header
固定24 位元組
長度Length Vector
表示後面有多少個Change Record
,每2 位元組
表示一個長度,計算長度時,需要進行4 位元組對齊
Change Record
就是具體的變化內容了,不同的操作有不同的格式。
使用 system dump
看下 Change Header
的資訊:
CHANGE #1 TYP:0 CLS:1 AFN:4 DBA:0x01000085 OBJ:73194 SCN:0x0000.000e606a SEQ:1 OP:11.2 ENC:0 RBL:0
其中主要欄位的含義:
- TYP: Change Type
- CLS: Class 等於 X$BH.CLASS 暫時不知用途
- DBA: Database Block Address,4位元組長度,
高10位
表示相對檔案號,低22位
表示塊號 - OP: 操作碼,區分操作型別,每個操作碼都由兩部分組成:
Layer Code
和Sub Code
,比如11.2
下圖是一些常用的操作
3.3 Transactions 事務
開始執行一個 DML
操作時,會建立一個 OP:5.2
的 Change,標識事務開始:
CHANGE #2 TYP:0 CLS:19 AFN:3 DBA:0x00c00090 OBJ:4294967295 SCN:0x0000.0010c5bb SEQ:3 OP:5.2 ENC:0 RBL:0
ktudh redo: slt: 0x0018 sqn: 0x0000033a flg: 0x0012 siz: 108 fbi: 0
uba: 0x00c007a0.009b.40 pxid: 0x0000.000.00000000
當事務提交或者回滾時,會建立一個 OP:5.4
的 Change,標識事務結束:
CHANGE #4 TYP:0 CLS:19 AFN:3 DBA:0x00c00090 OBJ:4294967295 SCN:0x0000.0010c5e6 SEQ:1 OP:5.4 ENC:0 RBL:0
ktucm redo: slt: 0x0018 sqn: 0x0000033a srt: 0 sta: 9 flg: 0x2 ktucf redo: uba: 0x00c007a0.009b.41 ext: 2 spc: 640 fbi: 0
一個完整的事務都有一個唯一標識,日誌中的體現就是 XID
:
xid: 0x0002.018.0000033a
XID
長度是 8 位元組
,由三部分組成:
- USN: Undo segment number (0x0002),目前不知如何獲取此值
- slt: Undo segment header transaction table slot (0x018),對應
ktudh/ktucm
中的slt
- sqn: 0x0000033a,對應
ktudh/ktucm
中的sqn
在 ktudh/ktucm
中有一個 uba
欄位,內容是 uba: 0x00c007a0.009b.41
,它表示此 Change
在 undo block
中的地址,長度是 7位元組
,也由三部分組成:
- undo block 的 DBA (0x00c007a0)
- 序號 (0x009b)
- 在 block 中的 Record 編號 (0x41)
下圖是一個完整事務的示例:
執行了兩個 update
,其中 c1=1
的 c2 原先等於 100,更新成了 101;c1=2
的 c2 原先等於 200,更新成了 201;
4. 相關命令
4.1 日誌歸檔和非歸檔
查詢資料庫當前的日誌模式:
SQL> archive log list;
or
SQL> select log_mode from v$database;
檢視線上日誌:
SQL> select l.STATUS, lf.MEMBER from v$log l, v$logfile lf where l.GROUP# = lf.GROUP#;
檢視已歸檔日誌:
SQL> select recid, stamp, thread#, sequence#, name from v$archived_log;
檢視預設歸檔路徑
SQL> show parameter db_recovery_file_dest;
日誌開啟歸檔模式:
SQL> shutdown immediate;
SQL> startup mount
SQL> alter database archivelog;
SQL> alter database open;
SQL> archive log list;
日誌關閉歸檔模式:
SQL> shutdown immediate;
SQL> startup mount
SQL> alter database noarchivelog;
SQL> alter database open;
4.2 Redo Log Dump
使用 ALTER SYSTEM
命令可以把二進位制的 Redo Log 檔案轉儲為任何文字編輯器可讀的 ASCII 編碼檔案,有助於我們理解分析二進位制結構,該命令的語法如下:
alter system dump logfile 'FileName'
scn min MinimumSCN
scn max MaximumSCN
time min MinimumTime (s)
time max MaximumTime (s)
layer Layer
opcode Opcode
dba min FileNumber BlockNumber
dba max FileNumber BlockNumber
rba min LogFileSequenceNumber BlockNumber
rba max LogFileSequenceNumber BlockNumber
objno ObjectNumber
xid UndoSegmentNumber UndoSlotNumber UndoSequenceNumber;
使用 SCN :
SQL> alter system dump logfile '/u01/app/oradata/orcl/redo03.log' scn min 1099234 scn max 1099246;
使用 RBA :
SQL> select cpodr_seq,cpodr_bno from x$kcccp where rownum=1;
CPODR_SEQ CPODR_BNO
---------- ----------
9 1514
SQL> DML (insert/update/delete)
SQL> select cpodr_seq,cpodr_bno from x$kcccp where rownum=1;
CPODR_SEQ CPODR_BNO
---------- ----------
9 1518
SQL> alter system dump logfile '/u01/app/oradata/orcl/redo03.log' rba min 9 1514 rba max 9 1518;
注意:每次 dump 後都需要退出此次會話,重新登入後再 dump,否則結果只會儲存到一個檔案內。
4.3 查詢 dump 路徑
有兩種辦法查詢 dump 路徑。
第一,使用以下命令檢視預設路徑:
SQL> show parameter user_dump_dest;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
user_dump_dest string c:\database\oracle\administrat
or\diag\rdbms\orcl\orcl\trace
第二,在執行 dump 命令前後使用以下命令,可以顯示:
SQL> oradebug setmypid;
SQL> alter system dump logfile xxxxxxx
SQL> oradebug tracefile_name;
c:\database\oracle\xxxxxxx\xxxxxxx.trc
4.4 dump 一個 insert 操作
首先,檢視當前使用的線上日誌是哪一個,即狀態為 CURRENT
的檔案:
SQL> select l.STATUS, lf.MEMBER from v$log l, v$logfile lf where l.GROUP# = lf.GROUP#;
然後,往 scott
使用者的 dept
表插入一條資料,檢視 dump 的結果,命令如下:
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
1099234
SQL> insert into scott.dept values(50, 'a', 'a');
SQL> commit;
SQL> select current_scn from v$database;
CURRENT_SCN
-----------
1099246
SQL> alter system dump logfile '/u01/app/oradata/orcl/redo03.log' scn min 1099234 scn max 1099246;
最後,二進位制 Redo log dump 的結果,這裡只摘出了 insert 部分,太長了,相信也沒人看~~:
CHANGE #1 TYP:0 CLS:1 AFN:4 DBA:0x01000085 OBJ:73194 SCN:0x0000.000e606a SEQ:1 OP:11.2 ENC:0 RBL:0
KTB Redo
op: 0x01 ver: 0x01
compat bit: 4 (post-11) padding: 0
op: F xid: 0x0002.018.0000033a uba: 0x00c007a0.009b.40
KDO Op code: IRP row dependencies Disabled
xtype: XA flags: 0x00000000 bdba: 0x01000085 hdba: 0x01000082
itli: 1 ispac: 0 maxfr: 4858
tabn: 0 slot: 0(0x0) size/delt: 10
fb: --H-FL-- lb: 0x1 cc: 3
null: ---
col 0: [ 2] c1 33
col 1: [ 1] 61
col 2: [ 1] 61
簡單解釋下,OP:11.2
表示這是一個 insert
操作;OBJ:73194
表示操作的表是 scott.dept
;最後三行的 col
表示操作的欄位資料,顯示的數值都是 16 進位制,其中 c1 33
按照一定的運算邏輯會轉成 50
,61
就是字元 a
的 ASCII 編碼。
5. 總結
建議上面的命令都手動執行下,別人總結的終究沒有自己經歷下,來的印象深刻。
本系列文章主要參考的有:
- Julian Dyke 對 RedoLog 分析的 PPT
- David Litchfield 對 Redo Logs 二進位制檔案剖析的 PDF
- zhoubihui 釋出在 GitHub 上的 redo_log_calculate_analysis 研究文章
以上資料網上均能搜尋到,當然了,您也可以關注下wx公眾號,「小創程式設計」回覆關鍵字「redolog」獲取。