1. 程式人生 > 資料庫 >Oracle RedoLog-基本概念和組成

Oracle RedoLog-基本概念和組成

Oracle 資料庫恢復操作最關鍵的依據就是 redo log,它記錄了對資料庫所有的更改操作。在研究如何提取 redolog 中 DML 操作的過程可謂一波三折,因為介紹 redolog 結構細節的資料實在太少了,不過好在最後大致理清了它的結構,並開發了一個基於日誌的同步軟體。

本系列文章就記錄下研究過程中遇到的問題和使用的分析命令、工具。

原文連結:https://www.chuonye.com/archives/oracle-redolog.html

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 採用迴圈覆蓋的方式寫入:當一個檔案寫滿後,開始寫入下一個,當最後一個檔案寫滿後,返回第一個開始寫入,如此迴圈。

LWR

這樣寫入會導致資料丟失嗎?預設情況下

資料庫日誌有兩種模式:歸檔非歸檔,非歸檔模式就會有覆蓋寫入的問題。在歸檔模式下,當一組 ReodLog 檔案寫滿,發生切換時,Oracle 會保證歸檔完成前此組檔案不被覆蓋。

archive-log

可以使用以下命令手動觸發日誌切換:

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 是按順序寫入的,基本格式如下:

redolog-format

前兩個塊記錄的是元資訊,分別是:

  • 第1個塊記錄檔案本身的資訊,比如檔案型別,塊大小和塊數,這部分稱為 File Header - 檔案頭
  • 第2個塊記錄資料庫例項相關資訊,比如資料庫SID,資料庫版本,這部分稱為 Redo Log Header - 重做日誌頭

結合日誌寫入方式,從整體上看,讀取這一組 Redo Log 檔案,在記憶體中可以把它們看成按塊為儲存單元的環形緩衝區來處理,解析的過程就是讀取一個個 Record

3.1 Redo Record

一個 Redo Record 可能佔用一個 block,也可能佔用多個 block,也可能只佔用 block 的一部分,這取決於它的長度,長度欄位就儲存在 Record Header 頭部,結構如下:

record-format

值得注意的是,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

其中:

  • Change Header 固定 24 位元組 長度
  • Length Vector 表示後面有多少個 Change Record,頭 2位元組表示長度列表的總長度,後面每 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

下圖是一些常用的操作

opcode

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)

下圖是一個完整事務的示例:

transactions

執行了兩個 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」獲取。