Oracle redo解析之-一、oracle redo log結構計算

做者囉嗦

  有疑問或合做能夠聯繫:zhou_bihui@qq.com.
  demo程序移步:github/demo ,redo log分析的幾個階段以下(Oracle 11g R2):git

  1. redo結構計算(已更新)
  2. BBED & DUMP 工具的使用(已更新)
  3. 常見change的分析(已更新)
  4. rowid計算(已更新)
  5. Oracle經常使用數據類型的底層存儲與轉換
  6. redo怎麼區分事務
  7. IMU模式與非IMU模式下redo record的不一樣
  8. undo segment、undo block和data block的聯繫
  9. 完整事務計算,並根據事務構建sql
  10. DDL語句產生的redo淺析

經驗之談

  若是是想最後能構建出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

redo文件結構

block 0的數據格式

  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
複製代碼

block1的數據格式

  第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
複製代碼

block2的數據格式

  從第三塊(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。工具

redo record header

  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

  記錄頭以後就是一組redo change了,一個操做對應一個change,好比插入,更新,塊清除等。每一個change對應一個操做碼(opcode)。
  每一個change又分爲change header,change length list(change向量表),和change body。

redo change header

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 length list

  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.

複製代碼
相關文章
相關標籤/搜索