Oracle RedoLog-基本概念和組成

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 R2Oracle 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 CodeSub 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,它表示此 Changeundo 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 按照一定的運算邏輯會轉成 5061 就是字符 a 的 ASCII 編碼。

5. 總結

建議上面的命令都手動執行下,別人總結的終究沒有自己經歷下,來的印象深刻。

本系列文章主要參考的有:

  • Julian Dyke 對 RedoLog 分析的 PPT
  • David Litchfield 對 Redo Logs 二進制文件剖析的 PDF
  • zhoubihui 發佈在 GitHub 上的 redo_log_calculate_analysis 研究文章

以上資料網上均能搜索到,當然了,您也可以關注下wx公衆號,「小創編程」回覆關鍵字「redolog」獲取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章