【文檔】五、Mysql Binlog事件結構

這個部分描述了事件被寫入binlog或者delay log中的屬性。所有的事件有相同的整體結構,也就是包含事件頭和事件數據:

+===================+
| event header      |
+===================+
| event data        |
+===================+

具體的內容隨着Mysql版本的升級而不同,這導致了binlog格式的不一致:
- v1:用於3.23版本
- v3:用於4.0.2到4.1版本
- v4:用於5.0及以上版本

v2的格式用於4.0.x的版本中,但是已經過期了,並且不再支持了。

有些事件的結構隨着版本沒有改變,而有些與版本有關。在所有的版本中,不同類型的事件在數據部分的結構不一樣。

日誌文件的第一個事件是特殊的。他是個符號事件,包含binlog版本和服務器版本。符號事件中的信息讓程序能夠決定日誌文件的格式,這樣剩餘的事件內容才能被正確的解析出來。

下面說明下不同版本的binlog格式:

v1事件結構:

+=====================================+
| event  | timestamp         0 : 4    |
| header +----------------------------+
|        | type_code         4 : 1    |
|        +----------------------------+
|        | server_id         5 : 4    |
|        +----------------------------+
|        | event_length      9 : 4    |
+=====================================+
| event  | fixed part       13 : y    |
| data   +----------------------------+
|        | variable part              |
+=====================================+

頭長度=13字節,數據長度=(事件長度-13)字節,y與事件類型有關。

v3事件結構

+=====================================+
| event  | timestamp         0 : 4    |
| header +----------------------------+
|        | type_code         4 : 1    |
|        +----------------------------+
|        | server_id         5 : 4    |
|        +----------------------------+
|        | event_length      9 : 4    |
|        +----------------------------+
|        | next_position    13 : 4    |
|        +----------------------------+
|        | flags            17 : 2    |
+=====================================+
| event  | fixed part       19 : y    |
| data   +----------------------------+
|        | variable part              |
+=====================================+

頭長度=19字節,數據長度=(事件長度-19)字節,y與事件類型有關。

v4事件結構

+=====================================+
| event  | timestamp         0 : 4    |
| header +----------------------------+
|        | type_code         4 : 1    |
|        +----------------------------+
|        | server_id         5 : 4    |
|        +----------------------------+
|        | event_length      9 : 4    |
|        +----------------------------+
|        | next_position    13 : 4    |
|        +----------------------------+
|        | flags            17 : 2    |
|        +----------------------------+
|        | extra_headers    19 : x-19 |
+=====================================+
| event  | fixed part        x : y    |
| data   +----------------------------+
|        | variable part              |
+=====================================+

頭長度=x字節,數據長度=(事件長度-x)字節,固定數據部分長度=y字節,變量數據程度=(事件長度-(x+y))字節。x由FDE(格式描述事件format description event)header_length給出。當前來說,x=19,所以extra_headers字段是空的。

y和事件類型有關,也是由FDE給出的。fixed-part的長度對於同一個事件類型是一樣的,但是不同的事件類型是不一樣的。

fixed-part有時候指的是post-header,變量部分有時候指的是payload或者body。

一、事件內容-寫入約定

事件內容的寫入約定如下:
- 數字是按照地位優先的格式寫入的(最不重要字節有限),除非顯式表示
- 表示位置或長度的值以字節形式寫入,並且是無符號的
- 有些數字是以封包整數的形式寫入的,這個格式後面再詳細說明
- 字符串有以下幾種格式:
- 字符串可能寫入一個固定長度域中,右邊以null填充(0x00)
- 可變長度的字符串前面包含一個表示字符串長度的內容
- 有些可變長度的字符串以null結尾,有些不是。每個字符串中有符號表示屬於哪種情況。
- 對於null結尾的字符串,最前面是字符串長度,這個長度不包含結尾的null字節,除非顯式表明
- 如果存在可變長度的字符串在事件結尾,並且事件前面沒有任何內容,這個字符串的長度=事件長度-事件中其他內容的長度。

有些事件使用封包整數,這是一個特殊的格式,用來有效的描述無符號整數。封包整數可以存儲一個8字節的整數,小整數(small integer)佔用1、3或4字節。根據下面的表格,第一個字節的值決定了如何讀這個數字:

第一個字節 格式
0-250 第一個字節就是數字(0-250),不需要額外的字節。
252 使用多於2字節,數字的範圍是251-0xffff
253 使用多於3字節,數字的範圍是0xffff-0xfffff
254 使用多於8字節,數字範圍是0xfffff-0xffffffffffffffff

二、事件頭字段

每個事件以LOG_EVENT_HEADER_LEN長度開頭,這個常量在v1格式中時13,在v3及以上格式中是19.

  • v1:13字節:timestamp+type code+server ID+event length
  • v3:19字節:v1的字段+next position+flags
  • v4:19字節或更多:v3的字段+可能有其他信息

v1事件頭:

+============================+
| timestamp         0 : 4    |
+----------------------------+
| type_code         4 : 1    |
+----------------------------+
| server_id         5 : 4    |
+----------------------------+
| event_length      9 : 4    |
+============================+

v1頭的13個字節也包含在其他版本的事件頭中。

v3事件頭

+============================+
| timestamp         0 : 4    |
+----------------------------+
| type_code         4 : 1    |
+----------------------------+
| server_id         5 : 4    |
+----------------------------+
| event_length      9 : 4    |
+----------------------------+
| next_position    13 : 4    |
+----------------------------+
| flags            17 : 2    |
+============================+

和v1對比,v3以上中的事件頭中包含兩個額外的字段,總長度爲19字節。

v4事件頭

+============================+
| timestamp         0 : 4    |
+----------------------------+
| type_code         4 : 1    |
+----------------------------+
| server_id         5 : 4    |
+----------------------------+
| event_length      9 : 4    |
+----------------------------+
| next_position    13 : 4    |
+----------------------------+
| flags            17 : 2    |
+----------------------------+
| extra_headers    19 : x-19 |
+============================+

v4的事件頭中包含一個extra_headers字段,是爲了以後擴展用。當前x=19,所以v4和v3當前格式一樣。

注意:extra_headers麼有在FORMAT_DESCRIPTION_EVENT或ROTATE_EVENT的頭中出現。

在log_event.h中,包含幾個事件頭的常量:
- EVENT_TYPE_OFFSET = 4
- SERVER_ID_OFFSET = 5
- EVENT_LEN_OFFSET = 9
- LOG_POS_OFFSET = 13
- FLAGS_OFFSET = 17

事件頭中包含以下信息:
- timestamp

4字節。表示的是語句開始執行的時間,格式與TIMESTAMP SQL數據類型一樣。

  • type_code

1字節。事件類型。1表示START_EVENT_V3,2表示QEURY_EVENT,以此類推。這些數字定義在log_event.h的Log_event_type的枚舉類中。

  • server id

4字節,產生這個事件的mysqld服務器id。這個是在服務器中配置文件配置的,用於主從複製。server id會在循環複製時避免死循環(開啓了–log-slave-updates配置)。假設M1、M2和M3的server id分別是1、2、3,而且他們循環複製:M1是M2的主,M2是M3的主,M3是M1的主。他們的主從關係如下:

M1---->M2
 ^      |
 |      |
 +--M3<-+

客戶端發起了一個插入語句給M1,M1執行了之後,寫入了M1的binlog文件中,包含了server id爲1。這個事件被髮給了M2,M2也執行了這個語句,然後寫binlog的時候,server id還是1,因爲這個事件最初是由M1發起的。然後M3也收到了這個事件,執行完成後寫入到M3的binlog文件時,server id還是1。然後M1收到了這個事件之後,在執行插入語句之前,可以分析出server id=1,也就是這個語句最初是本機發起的,這個語句會被忽略執行。

  • event_length

4字節。事件的總長度,包含事件頭和事件數據。大部分的事件小於1000字節,除非使用LOAD DATA INFILE(因爲包含加載文件,所以他們可能很大)

  • next_position(v1不包含):4字節。下個時間在master的binlog中的位置。這個格式在binlog和relay log中不一樣,而且與server版本有關(對於relay log來說,與master的版本有關)

    • v3版本的binlog:事件開頭的偏移量,從binlog文件的開頭開始計算。也就是說,在事件寫入之前,等於tell()的值。
      所以binlog的第一個事件的next_position=4,對於事件n和n+1,next_position(n+1)=next_position(n)+event_length(n)。
    • v3版本的relay log,master是v1:可能是0,但是沒法測試,因爲現在已經基本沒有3.23的服務器了。
    • v3版本的relay log,master是v3:開頭事件的偏移量和master中binlog文件一樣,也是從master的binlog文件開頭開始計算。
    • v4版本的binlog:事件結尾的偏移量,從binlog文件開頭開始計算。也就是說,等於在事件被寫入之後,正好等於tell()的值。所以binlog中第一個事件的next_position=4+event_length,對於事件n和n+1,next_position(n+1)=next_position(n)+event_length(n+1)。
  • flags(v1中沒有)

2字節,詳見下一節。

  • extra_headers(v1和v3中不存在)

可變大小,當前爲0。

事件flag

對於v3及以上版本中,事件頭中包含一個2字節的時間flag在FLAGS_OFFSET=17位置上。在log_event.h中並沒有定義。

當前的事件flag:
- LOG_EVENT_BINLOG_IN_USE_F=0x1(5.0.3新增)

表示一個binlog文件是否正確的被關閉了。這個標誌位只對FORMAT_DESCRIPTION_EVENT生效。當這個事件被寫入日誌文件時,纔會設置這個標誌位。當日志文件後續被關閉後,這個標誌位會被清除掉。(這是Mysql修改已經寫完的binlog文件的唯一情況)。

  • LOG_EVENT_THREAD_SPECIFIC_F=0x4(4.1.0新增)

僅供mysqlbinlog使用,使他能夠正確的處理臨時表。mysqlbinlog把binlog中的事件打印出來,讓你能夠理解打印出來的內容。但是如果兩個獨立的線程使用同樣的臨時表名,比如:

<thread id 1>
CREATE TEMPORARY TABLE t (a INT);
<thread id 2>
CREATE TEMPORARY TABLE t (a INT);

這種情況下,簡單的執行sql語句,會導致表t已經存在的錯誤。所以使用臨時表的事件需要設置這個標誌位,那樣mysqlbinlog知道需要在變量值錢設置假的線程id,例如:

SET PSEUDO_THREAD_ID=1;
CREATE TEMPORARY TABLE t (a INT);
SET PSEUDO_THREAD_ID=2;
CREATE TEMPORARY TABLE t (a INT);

這樣,服務器接收到命令後就沒有歧義了。所有情況下都打印SET PSEUDO_THREAD_ID,及時臨時表沒有用到,這樣不會產生bug,但是會有點慢。

  • LOG_EVENT_SUPPRESS_USE_F=0x8(4.1.7新增)

在一個語句被記錄前,抑制產生USE語句。用於任何不需要使用默認數據庫的事件中,比如CREATE DATABASE和DROP DATABASE。

  • LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F=0x10(5.1.4新增)

在事件寫入日誌文件中後,導致binlog內部的表映射版本增加。

過時的標誌位:

  • LOG_EVENT_TIME_F(4.1.1過期)。從未被設置過
  • LOG_EVENT_FORCED_ROTATE_F(4.1.1過期):這個標誌位是在master的ROTATE_EVENT中配置的,但是沒有任何用處。

三、事件數據字段(事件詳細信息)

事件數據部分的結構依賴於事件類型:

  • v1和v3版本中,事件類型完全決定了數據格式
  • v4中,數據部分的解析除了依賴於事件類型,還依賴於格式描述事件的信息。這是因爲在v4中,允許包含一個extra headers字段,這個字段的大小是在格式描述事件中定義的。實際上,當前這個字段是空的。

數據部分包含固定大小和可變大小兩部分。兩部分都可以爲空,這由事件類型決定。(比如,STOP_EVENT只包含頭,數據內容都爲空)。

事件數據的大小=事件大小-事件頭大小。下面的規則對binlog中所有的事件都通用:
- 對於所有相同類型的事件,固定部分的大小一樣。
- 對於所有相同類型的事件,可變部分的大小可能不一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章