1. 程式人生 > >MySQLbinlog日誌04binlog日誌字節碼解析之二Write_Rows事件

MySQLbinlog日誌04binlog日誌字節碼解析之二Write_Rows事件

註釋 sql數據庫 bsp class header alt top 函數 都是

本系列博客主要介紹MySQL數據庫的binlog日誌的相關內容,這個系列的主題包括:

MySQLbinlog日誌01binlog日誌基本操作

MySQLbinlog日誌02binlog日誌用於數據恢復

MySQLbinlog日誌03binlog日誌字節碼解析

MySQLbinlog日誌04binlog日誌字節碼解析之二Write_Rows事件

前一篇博客介紹了

MySQLbinlog日誌03binlog日誌字節碼解析

本篇博客將接著介紹Write Rows事件的字節碼解析。

7.Write rows事件

現在來解析insert語句對應的核心binlog事件:Write rows事件。這個事件用於

insert/update語句產生的增加和修改行數據的記錄。每個Write rows事件只涉及對一行數據的增加或者修改,盡管這個事件的名字用了復數形式。

技術分享圖片

對應的字節碼數據如下所示:

技術分享圖片

common header的內容如下:

時間戳:

技術分享圖片

字段

字節碼

時間戳

8191a35b

2018-09-20 20:24:33

事件類型

1e

30

MySQL server-id

65000000

101

本事件的長度

3b000000

59

下一個事件的開始位置

5e140000

5214

標誌

0000

0

Write rows事件的事件相關頭結構格式還是從源代碼註釋中找到的,總體結構如下:

技術分享圖片

各個條目細分後的具體含義如下:

條目

長度

事件

偏移

備註

id

6

0

標誌

2

6

var_header_len

2

8

附加行數據

取決於var_header_len的值

列的個數

2

8

Npacked integer

列標誌

變長

INT((N + 7) / 8,每個列1bit

操作前列數據標誌

變長

定位行數據用到的列:INSERT/DELETE

操作後列數據標誌

變長

修改後的列:UPDATE,本例中沒這部分。

行數據

變長

是否為NULL標誌+1的值+2的值+...

Write rows事件自身的字節碼數據如下:

技術分享圖片

id6個字節,值為120

標誌占2個字節,值為1

var_header_len2個字節,值為2字節。包含了自身的長度以及附加行數據的長度。因此這個事件中沒有附加行數據。

接著是列的個數,這個整數的編碼規則比較復雜。下面這個代碼用於讀取這樣的列的個數的字節碼。具體代碼如下所示:

技術分享圖片

這個事件中第1個字節為06,因此列的數量就是6個列,僅僅占用1個字節。

接著是操作前的行記錄用到的列的標誌。對於INSERT語句而言,不包含這一部分。對於UPDATE/DELETE而言,就是mysqlbinlog輸出的WHERE中用到哪些列。因此具體格式在操作後的部分進行描述。

接著是操作後的行記錄用到的列的標誌。長度是(6+7)/8=1個字節,值為ff,只有6個二進制位有效。可以看到這個6個字段都被使用到了。這1個字節僅僅是標誌位,不是實際的列數據。對於INSERT語句而言,就是新增的記錄數據中包含哪些列。對於UPDATE而言,就是SET中用到哪些列。

最後是真正的行記錄的列數據。因為在common header中已經知道了整個事件的長度,而此時前面這些部分的長度也已經確定了,那麽列數據的長度也可以計算出來。實際上就是分析到此時的偏移量開始,到事件結束位置的前1字節為止的這個範圍內的字節碼。

技術分享圖片

列數據具體是怎麽存儲的,稍後介紹。

行數據先經過pack_row()函數進行組裝後才寫入到binlog文件中。pack_row()函數的代碼經過精簡後如下所示:

技術分享圖片

先存儲NULL標記,即值為NULL的列標記,每個列用一個位來表示其後的值是否為NULL

t1表只有6個列,因此這裏值為NULL的列只可能占用1個字節。當前事件中這個值為0xc0

最後6位全部是0,這6個列當前的值全部不為NULL

技術分享圖片

接著依次存儲每個值不是NULL的列的具體數據。

每一種類型的列都有對應的pack()函數。前面已經知道了t1表的6個列中,第2列(name)是varchar之外,其它列都是intint類型的列在MySQL源代碼中定義為Field_long類。

技術分享圖片

功能主要就是這個32位整數存儲為Big endian格式的字節碼,占用4個字節。

varchar類型的列對應的類是Field_varstring

技術分享圖片

對於varstring,先存儲字符串的長度,再存儲實際的字符串。整數類型的列的存儲方式和前面介紹的各種長度的存儲方式是類似的,都可以認為是big-endian格式,而這裏的字符串長度,是按照little-endian格式存儲的。小於255字節,存儲為1個字節,否則存儲為2個字節。

現在來解析這個Write rows事件中包含的列的值,即行記錄的具體數據。

技術分享圖片

字節碼

NULL標記

c0

11000000.6列的值均不為NULL

1

0200 0000

INT2

2

024132

VARSTRINGA2

3

1500 0000

INT:21

4

1600 0000

INT:22

5

1700 0000

INT:23

6

1800 0000

INT:24

這個結果與使用mysqlbinlog提取的結果是一致的:

技術分享圖片

最後面4個字節是這個binlog事件的校驗碼。

至此,INSERT語句對應的Write row事件的字節碼解析完畢。UPDATEDELETE語句對應的binlog事件的字節碼解析方式跟這個非常類似,就不再贅述了。

MySQL數據庫binlog系列博客就是這些內容了。

MySQLbinlog日誌04binlog日誌字節碼解析之二Write_Rows事件