897
技術社區[雲棲]
【文檔】五、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中所有的事件都通用:
- 對於所有相同類型的事件,固定部分的大小一樣。
- 對於所有相同類型的事件,可變部分的大小可能不一樣。
最後更新:2017-10-09 12:03:12