有疑問或合做能夠聯繫:zhou_bihui@qq.com.
demo程序移步:github/demo ,redo log分析的幾個階段以下(Oracle 11g R2):git
若是是想最後能構建出SQL語句,應該按如下步驟:
1. redo log文件的結構能區分,即計算redo record,從redo record中計算出change,經過大量的archive log文件作大量的測試。
2. 計算每一個change,先能構建簡單的增刪改,這裏先不要考慮事務。
3. 分析事務,這裏須要判斷事務的commit和rollbak,構建的話只須要構建commit的事務。
4. 以後也能夠繼續學習構建DDL語句,也能夠嘗試作些小demo,例如保持兩臺oracle的數據一致,能夠經過解析主機的redo log,將sql傳到備機執行。github
要從oracle redo日誌中反編譯出sql,有如下3個步驟:
1. 從redo log中將redo block header、redo record、redo record header、redo change、redo change header分離出來。
2. 根據每個redo change中的opcode肯定change的操做類型。
3. 根據xid區分事務。
sql
block 0是redo的第一個數據塊,記錄了塊大小,塊總數等信息。須要注意的是,block不包含redo block header(即塊頭),不包含在塊總數中。數據庫
typedef struct file_header_0{
uint32_t unknown0[5];
unit32_t blocksize; //塊大小,512/1024...
unit32_t blockcount; //當前文件的總塊數
unit32_t unknown1[2];
unit32_t zero[119];
}Redo_fh0
複製代碼
第2塊作爲數據庫頭,包含數據庫信息(如版本號、數據庫ID、文件序號等)。bash
tyoedef struct redo_block_header{
uint32_t signature; //簽名
unit32_t blocknum; //塊號
unit32_t sequence; //順序號
unit16_t offset; //當前塊的第一個redo record開始的位置,最高位捨棄
unit16_t checksum; //塊的checksum,寫入時更新
}Redo_bh
typedef struct RBA{ //redo文件中的位置信息
unit32_t sequence;
unit32_t blocknum;
unit16_t offset;
}Redo_RBA
typedef struct scn{
unit32_t scnbase;
unit16_t scnwrapper;
}Redo_scn
typedef struct file_header_1{
Redo_bh blockheader;
unit32_t unknown0;
unit32_t Compatibility Vsn;
unit32_t db id;
unit64_t db name; //猜想用來表示數據庫名稱
unit32_t control seq;
unit32_t file size;
unit32_t blksize;
unit16_t file number;
unit16_t file type;
unit32_t activation id;
unit8_t zero[36];
unit8_t unknown1[64];
unit32_t nab; //當前文件最後一個有真實記錄塊的下一個塊
unit32_t resetlogs count;
Redo_scn resetlos scn;
unit16_t 0;
unit32_t hws;
unit32_t thread;
Redo_scn low scn;
unit16_t 0;
unit32_t low scn time;
Redo_scn next scn;
unit16_t 0;
unit32_t nex scn time;
unit32_t unknown2;
Redo_scn enabled scn;
unit16_t 0;
unit32_t enabled scn time;
Redo_scn thread closed scn;
unit16_t 0;
unit32_t thread closed scn time;
unit8_t unknown3[52];
Redo_scn prev resetlogs scn;
unit16_t 0;
unit32_t prev resetlogs count;
unit8_t unknown4[216]
}Redo_fh1
複製代碼
從第三塊(block 2)開始,塊裏面存儲着Oracle的redo日誌。由塊頭和塊體構成,其中塊頭即結構體Redo_bh,和block1的塊頭結構一致。oracle
tyoedef struct redo_block_header{
uint32_t signature; //簽名
unit32_t blocknum; //塊號
unit32_t sequence; //順序號
unit16_t offset; //當前塊的第一個redo record開始的位置,最高位捨棄
unit16_t checksum; //塊的checksum,寫入時更新
}Redo_bh
typedef struct block{
Redo_bh blockheader;
unit8_t redo record[496]; //redo record,日誌記錄
}
複製代碼
塊頭分析:
1. signature -- 表示這是一個redo block。
2. block number -- 當前塊的編號。
3. sequence -- 順序號(序列號),即v$log視圖的SEQUENCE#字段
4. offset -- 標記本快中第一個redo record開始的位置,位置是包括塊頭的,且須要過濾最高位。
5. checksum -- 校驗值,塊寫入時計算獲得。app
操做記錄(redo record)的結構:
1. 一個記錄頭(redo record header)
2. 多個change。工具
record header的長度有24字節和68字節兩種狀況。學習
typedef struct record_header0{
unit32_t len; //redo record的長度
unit8_t vld;
unit8_t unknown0;
unit16_t record header scn wrapper;
unit32_t record header scn base;
unit16_t subscn;
}
typedef struct record_header1{
unit8_t unknown2[10]:
}Redo_rh24 //長度爲24字節的record header結構
typedef struct record_header2{
unit8_t unknown0[50];
unit32_t timestamp; //本次操做的時間戳
}Redo_rh68 //長度爲68字節的record header結構
複製代碼
如何肯定record header的長度是24字節仍是68字節?
通過屢次測試以及查找大神的文檔,得出一個結論,且這個結論至今還未出錯。在一位大神的文檔中看到說record header的長度由vld決定,在11G版本下,我測試發現,只要vld的值中包括了4,長度就是68字節,反之是24字節。包括4的意思是,假設vld是12,4+8=12,說明vld包括了4,因此長度是68字節。vld的取值由如下表格肯定:測試
Mnemonic | Value | Description |
---|---|---|
KCRVOID | 0 | The contents are not valid. |
KCRVALID | 1 | Includes change vectors |
KCRDEPND | 2 | Includes commit SCN |
KCRVOID | 4 | Includes dependent SCN |
KCRNMARK | 8 | New SCN mark record. SCN allocated exactly at this point in the redo log by this instance |
KCRMARK | 16 | Old SCN mark record. SCN allocated at or before this point in the redo. May be allocated by another instance |
KCRORDER | 32 | New SCN was allocated to ensure redo for some block would be ordered by inc/seq# when redo sorted by SCN |
表格的含義我不太清楚,得出的結論是我屢次驗證的來的。
記錄頭以後就是一組redo change了,一個操做對應一個change,好比插入,更新,塊清除等。每一個change對應一個操做碼(opcode)。
每一個change又分爲change header,change length list(change向量表),和change body。
typedef struct opcode{
unit8_t layer number;
unit8_t code;
}Redo_opcode
typedef struct change header{
Redo_opcode opcode;
unit16_t CLS;
unit16_t AFN;
unit16_t OBJ0;
unit32_t DBA;
unit32_t change header base;
unit16_t change header wrapper;
unit16_t unknown0;
unit8_t SEQ;
unit8_t TYP;
unit16_t OBJ1;
}Redo_ch //長度固定爲24字節
複製代碼
計算OBJ(這裏的OBJ是data_object_id,不是object_id):OBJ1 | (OBJ0 << 16)
例如:OBJ0=0x0001, OBJ1=0x0388
OBJ0左移16位:00000000 00000001 00000000 00000000
OBJ1:00000011 10001000
或的結果:00000000 00000001 00000011 10001000
轉成十進制:66440
OBJ爲本次操做的表的data_object_id,能夠根據OBJ從數據字典中查到表名,字段等信息用來構建SQL語句。
複製代碼
redo change header後是一個change向量表,主要是用來計算change的長度。change向量表的長度按4字節對齊,而後內部是每2字節一段。第1,2字節表示向量表總長度(未進行4字節對齊以前的有效長度),總長度以後剩餘的長度(有效總長度-2),分爲每2字節一段,每一段表示一個change部分的長度,這些長度會按順序寫在向量表以後,向量表中的長度爲change部分的有效長度。
向量表是變長的,和redo record同樣,長度在最開始,所以每次都應該先讀取表示有效長度的2字節進行解析。
例:change length list: 0a004800 31000800 0100c000
1. 向量表總長度:0x000a = 10,有效長度是10,補齊4字節後是12,所以最後的2字節c000是無效字節
2. 有效長度減去1,2字節後剩8字節,每2字節爲一段,說明這個change被分紅了4部分
3. 第一部分有效長度: 0x0048 = 72, 第二部分有效長度: 0x0031 = 49, 第三部分有效長度: 0x0008 = 8, 第四部分有效長度: 0x0001 = 1
4. 第一部分實際長度: 72字節, 第二部分實際長度: 52字節, 第三部分實際長度: 8字節, 第四部分實際長度: 4字節
5. 整個change的實際長度: 24(header) + 12(length list) + 72 + 52 + 8 + 4 = 172字節
複製代碼
[注意]本機是小端機器,高字節在前,低字節在後。
Block2:
1. offset:0x8010,過濾最高位,0x0010=16,表示Block2的第一個redo record從16字節開始。
2. record len:0x00000100 = 256,說明record的長度爲256.
3. vld:在record的偏移量是4字節,vld自己的長度是1字節,從Block2中能夠獲得vld=0x05=5(1+4),說明當前record的record header長度爲68字節。
4. 68字節後,是record的第一個change[即6行的05021900處],change header固定爲24字節。
5. 24字節後,是change length list,即:04002000,當前change只有一個部分,長度爲0x0020=32字節。
6. 計算獲得,第一個change的長度爲:24(change header)+4(length list)+32=60字節。
7. 從第6行的05021900處開始數60字節,接着的是第二個change,即8行的05011a00處。
8. 24字節後是第二個change的length list:06001400 4c000000,length list的有效長度是6字節,當前change被分紅了兩部分,第一部分長度0x0014=20字節,第二部分長度=0x004c=76字節。
9. 計算獲得,第二個change的長度爲:24+8(補齊4字節的倍數)+20+76=128字節。
10. 到這裏,第一個record結束了,接着是第二個record。
11. 第二個record,從12行的3c000000處開始,這裏回到第2步,從計算record len開始,同理。
Block3:
1. 0ffset:0x8040,過濾最高位0x0040=64,說明Block3的第一個record開始於64字節處,而16字節-64字節之間的內容屬於上一個Block的跨塊record記錄。
2. 日後的計算,同Block2.
複製代碼