本系列博客主要介紹MySQL資料庫的binlog日誌的相關內容,這個系列的主題包括: MySQLbinlog日誌01binlog日誌基本操作 MySQLbinlog日誌02binlog日誌用於數據恢復 MySQLbinlog日誌03binlog日誌位元組碼解析 MySQLbinlog日誌04binlo ...
本系列博客主要介紹MySQL資料庫的binlog日誌的相關內容,這個系列的主題包括:
MySQLbinlog日誌04binlog日誌位元組碼解析之二Write_Rows事件
前一篇博客介紹了
本篇博客將接著介紹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 |
N:packed integer |
列標誌 |
變長 |
|
INT((N + 7) / 8,每個列1個bit。 |
操作前列數據標誌 |
變長 |
|
定位行數據用到的列:INSERT/DELETE |
操作後列數據標誌 |
變長 |
|
修改後的列:UPDATE,本例中沒這部分。 |
行數據 |
變長 |
|
是否為NULL標誌+列1的值+列2的值+... |
Write rows事件自身的位元組碼數據如下:
表id占6個位元組,值為120。
標誌占2個位元組,值為1。
var_header_len占2個位元組,值為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之外,其它列都是int。int類型的列在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 |
INT:2 |
第2列 |
024132 |
VARSTRING:A2 |
第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事件的位元組碼解析完畢。UPDATE和DELETE語句對應的binlog事件的位元組碼解析方式跟這個非常類似,就不再贅述了。
MySQL資料庫binlog系列博客就是這些內容了。