MySql Binlog事件和二進(jìn)制文件解析

START_EVENT_V3

每個(gè)binlog文件開(kāi)始的時(shí)候?qū)懭氲氖录狗伲耸录挥迷贛ySQL3.23 – 4.1夸楣,MYSQL5.0以后已經(jīng)被FORMAT_DESCRIPTION_EVENT取代碴里。

QUERY_EVENT

執(zhí)行更新語(yǔ)句時(shí)會(huì)生成此事件,包括:create谈火,insert筒占,update,delete蛛株;

手動(dòng)觸發(fā):

insert into btest values(1,100,'zhaohui');

| bin-log.000001 | 432 | Query       |         1 |         536 | use `test`; insert into btest values(1,100,'zhaohui')                                                                                                                                                          |
| bin-log.000001 | 536 | Xid         |         1 |         563 | COMMIT /* xid=30 */  
ROTATE_EVENT

當(dāng)mysqld切換到新的binlog文件生成此事件团赁,切換到新的binlog文件可以通過(guò)執(zhí)行flush logs命令或者binlog文件大于max_binlog_size參數(shù)配置的大小谨履;

mysql> flush logs;
Query OK, 0 rows affected (0.24 sec)
 
mysql> show binlog events in 'bin-log.000002';
+----------------+-----+-------------+-----------+-------------+---------------------------------------+
| Log_name       | Pos | Event_type  | Server_id | End_log_pos | Info                                  |
+----------------+-----+-------------+-----------+-------------+---------------------------------------+
| bin-log.000002 |   4 | Format_desc |         1 |         107 | Server ver: 5.5.29-log, Binlog ver: 4 |
| bin-log.000002 | 107 | Rotate      |         1 |         148 | bin-log.000003;pos=4                  |
+----------------+-----+-------------+-----------+-------------+---------------------------------------+
INTVAR_EVENT

當(dāng)sql語(yǔ)句中使用了AUTO_INCREMENT的字段或者LAST_INSERT_ID()函數(shù)欢摄;此事件沒(méi)有被用在binlog_format為ROW模式的情況下。

insert into btest (age,name)values(100,'zhaohui');
 
mysql> show binlog events in 'bin-log.000003';
+----------------+-----+-------------+-----------+-------------+---------------------------------------------------------------+
| Log_name       | Pos | Event_type  | Server_id | End_log_pos | Info                                                          |
+----------------+-----+-------------+-----------+-------------+---------------------------------------------------------------+
| bin-log.000003 |   4 | Format_desc |         1 |         107 | Server ver: 5.5.29-log, Binlog ver: 4                         |
| bin-log.000003 | 107 | Query       |         1 |         175 | BEGIN                                                         |
| bin-log.000003 | 175 | Intvar      |         1 |         203 | INSERT_ID=2                                                   |
| bin-log.000003 | 203 | Query       |         1 |         315 | use `test`; insert into btest (age,name)values(100,'zhaohui') |
| bin-log.000003 | 315 | Xid         |         1 |         342 | COMMIT /* xid=32 */                                           |
+----------------+-----+-------------+-----------+-------------+---------------------------------------------------------------+
5 rows in set (0.00 sec)

btest表中的id為AUTO_INCREMENT笋粟,所以產(chǎn)生了INTVAR_EVENT

TABLE_MAP_EVENT

用在binlog_format為ROW模式下剧浸,將表的定義映射到一個(gè)數(shù)字,在行操作事件之前記錄(包括:WRITE_ROWS_EVENT矗钟,UPDATE_ROWS_EVENT,DELETE_ROWS_EVENT)嫌变;

mysql> insert into btest values(998,88,'zhaohui');
Query OK, 1 row affected (0.09 sec)
 
mysql> show binlog events in 'bin-log.000004';
+----------------+-----+-------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Log_name       | Pos | Event_type  | Server_id | End_log_pos | Info                                                                                                                                                                                                                           |
+----------------+-----+-------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
......                                                                                                                                                                                                         |
| bin-log.000004 | 776 | Query       |         1 |         844 | BEGIN                                                                                                                                                                                                                          |
| bin-log.000004 | 844 | Table_map   |         1 |         892 | table_id: 33 (test.btest)                                                                                                                                                                                                      |
| bin-log.000004 | 892 | Write_rows  |         1 |         943 | table_id: 33 flags: STMT_END_F                                                                                                                                                                                                 |
| bin-log.000004 | 943 | Xid         |         1 |         970 | COMMIT /* xid=20 */                                                                                                                                                                                                            |
+----------------+-----+-------------+-----------+-------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
14 rows in set (0.00 sec)

WRITE_ROWS_EVENT吨艇、UPDATE_ROWS_EVENT和DELETE_ROWS_EVENT

以上三個(gè)事件都被用在binlog_format為ROW模式下,分別對(duì)應(yīng)inset腾啥,update和delete操作东涡;

XID_EVENT

支持XA的存儲(chǔ)引擎才有,本地測(cè)試的數(shù)據(jù)庫(kù)存儲(chǔ)引擎是innodb倘待,所有上面出現(xiàn)了XID_EVENT疮跑;innodb事務(wù)提交產(chǎn)生了QUERY_EVENT的BEGIN聲明,QUERY_EVENT以及COMMIT聲明凸舵,
如果是myIsam存儲(chǔ)引擎也會(huì)有BEGIN和COMMIT聲明祖娘,只是COMMIT類型不是XID_EVENT;

Binlog事件數(shù)據(jù)

1.QUERY_EVENT
執(zhí)行更新語(yǔ)句時(shí)會(huì)生成此事件啊奄,包括:create渐苏,insert,update菇夸,delete琼富;

Fixed data part,總長(zhǎng)度13字節(jié):
4字節(jié):執(zhí)行sql的線程id庄新;
4字節(jié):執(zhí)行sql的時(shí)間鞠眉;
1字節(jié):數(shù)據(jù)庫(kù)名稱的長(zhǎng)度薯鼠;
2字節(jié):執(zhí)行sql產(chǎn)生的錯(cuò)誤碼;
2字節(jié):狀態(tài)變量的長(zhǎng)度械蹋,具體內(nèi)容在Variable part出皇;
Variable part:
可變字節(jié):狀態(tài)變量,每個(gè)狀態(tài)變量key為一個(gè)字節(jié)朝蜘,后面跟著value恶迈,不同的key對(duì)應(yīng)不同長(zhǎng)度的value,但是總長(zhǎng)度在Fixed data part中已經(jīng)定義谱醇;
可變字節(jié):數(shù)據(jù)庫(kù)名稱
可變字節(jié):sql語(yǔ)句暇仲,通過(guò)事件的總長(zhǎng)度-header長(zhǎng)度-Fixed data-狀態(tài)變量,剩余的字節(jié)數(shù)組通過(guò)utf-8編碼即可獲雀笨省奈附;

TABLE_MAP_EVENT

| bin-log.000004 |  844 | Table_map   |         1 |         892 | table_id: 33 (test.btest)

將表的定義映射到一個(gè)數(shù)字,在行操作事件之前記錄(包括:WRITE_ROWS_EVENT煮剧,UPDATE_ROWS_EVENT斥滤,DELETE_ROWS_EVENT);

Fixed data part:
6字節(jié):表Id勉盅;
2字節(jié):保留字段為將來(lái)使用佑颇;
Variable part:
1字節(jié):數(shù)據(jù)庫(kù)名字的長(zhǎng)度;
可變字節(jié):數(shù)據(jù)庫(kù)名字草娜,根據(jù)前一個(gè)字節(jié)記錄的名字長(zhǎng)度挑胸,獲取的字節(jié)數(shù)組通過(guò)utf-8編碼即可獲取宰闰;
1字節(jié):表名的長(zhǎng)度茬贵;
可變字節(jié):表名,根據(jù)前一個(gè)字節(jié)記錄的名字長(zhǎng)度移袍,獲取的字節(jié)數(shù)組通過(guò)utf-8編碼即可獲冉庠濉;
Packed integer:用來(lái)記錄表中字段的數(shù)量葡盗;

mysql事件太多螟左,這里就不一一介紹。

binlog二進(jìn)制文件

先說(shuō)一下觅够,binlog的結(jié)構(gòu)路狮。文件頭由一個(gè)四字節(jié)Magic Number構(gòu)成,其值為1852400382蔚约,在內(nèi)存中就是"0xfe,0x62,0x69,0x6e"奄妨。這個(gè)Magic Number就是來(lái)驗(yàn)證這個(gè)binlog文件是否有效 。

在文件頭之后苹祟,跟隨的是一個(gè)一個(gè)事件依次排列砸抛。下面具體來(lái)看下:

binlog生成

在MySQL中執(zhí)行flush logs评雌,并執(zhí)行以下操作。

mysql> create database abcd;

Query OK, 1 row affected (0.01 sec)



mysql> create table test (a1 int primary key not null auto_increment, a2 double not null, a3 timestamp, a4 datetime, a5 char(10), a6 varchar(4000), a7 text);

Query OK, 0 rows affected (0.21 sec)



mysql> insert into test values(1, 2.222222222, now(), now(), 'abc', 'abcdefghasdasdasd', 'qwetrhyokxocm3479thcms9q25hdr9ker8thcfisdrhoc');

Query OK, 1 row affected (0.08 sec)



mysql> update test set a1=10, a2=3.33333, a3=now(), a4=now(), a5='abcde', a6='a', a7='s' where a1=1;

Query OK, 1 row affected (0.06 sec)

Rows matched:1  Changed: 1  Warnings: 0



mysql> delete from test;

Query OK, 1 row affected (0.04 sec)



mysql> drop table test;

Query OK, 0 rows affected (0.22 sec)



mysql> flush logs;

Query OK, 0 rows affected (0.17 sec)

Binlog文件構(gòu)成

對(duì)于該binlog而言直焙,主要包括以下幾種event景东。

FORMAT_DESCRIPTION_EVENT

GTID_LOG_EVENT/ANONYMOUS_GTID_LOG_EVENT

QUERY_EVENT

TABLE_MAP_EVENT

WRITE_ROW_EVENT/UPDATE_ROW_EVENT/DELETE_ROW_EVENT

XID_EVENT

ROTATE_EVENT

除這些外還有許多其他類型event,但多數(shù)不常見(jiàn)或隨著binlog版本的升級(jí)而棄用奔誓。每個(gè)event包括Binlog event header斤吐,Post-Header和Body三部分。其中對(duì)于所有event而言厨喂,Binlog event header的長(zhǎng)度與格式是相同的和措,之后會(huì)在FORMAT_DESCRIPTION_EVENT部分做統(tǒng)一介紹。Post-Header對(duì)于同類型的event是長(zhǎng)度相同的蜕煌,部分類型的event并沒(méi)有Post-Header派阱。Body則為event中的變量部分

下面將詳細(xì)介紹每一種event在binlog中的解析方式。

FORMAT_DESCRIPTION_EVENT

該部分位于整個(gè)文件的頭部斜纪,每個(gè)binlog文件都必定會(huì)有唯一一個(gè)該event贫母。

二進(jìn)制文件內(nèi)容


image.png

解析的binlog

/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;

/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;

DELIMITER /*!*/;

# at 4

#180321 18:00:32 server id 1813309  end_log_pos 123 CRC32 0x77f16c93    Start: binlog v 4, server v 5.7.17-log created 180321 18:00:32

BINLOG '

QC2yWg89qxsAdwAAAHsAAAAAAAQANS43LjE3LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAXwAEGggAAAAICAgCAAAACgoKKioAEjQA

AZNs8Xc=

'/*!*/;

Binlog event header部分:
fe 62 69 6e:文件前四位非Binlog event header部分,而是magic number盒刚,在這里指代.bin腺劣,代表文件的格式。

后面的Header總長(zhǎng)固定為19個(gè)字節(jié)因块。

40 2d b2 5a:事件發(fā)生的時(shí)間戳誓酒。應(yīng)用服務(wù)器是X86架構(gòu),在存儲(chǔ)的時(shí)候?yàn)樾《四J街簦吹臀蛔止?jié)排放在內(nèi)存的低地址端,也即實(shí)際是5a b2 2d 40代表一個(gè)數(shù)值(時(shí)間戳)寨辩,解析成時(shí)間為2018/3/21 18:00:32吓懈。

0f:type_code,代表該事件的類型靡狞,幾個(gè)重要的事件類型為:

0x02 QUERY_EVENT

0x04 ROTATE_EVENT

0x0f FORMAT_DESCRIPTION_EVENT

0x10 XID_EVENT

0x13 TABLE_MAP_EVENT

0x1d ROWS_QUERY_EVENT

0x1e WRITE_ROWS_EVENTv2

0x1f UPDATE_ROWS_EVENTv2

0x20 DELETE_ROWS_EVENTv2

0x21 GTID_EVENT

0x22 ANONYMOUS_GTID_EVENT

0x23 PREVIOUS_GTIDS_EVENT

3d ab 1b 00:server_id耻警,就是MySQL配置cnf文件中的server_id。這里是00 1b ab 3d甸怕,代表1813309甘穿。

77 00 00 00:代表整個(gè)event的長(zhǎng)度,長(zhǎng)度為119梢杭。
7b 00 00 00:下一個(gè)event開(kāi)始的位置温兼,可以看到與前一個(gè)值剛好差4,也就是魔數(shù)的長(zhǎng)度武契。

00 00:flags募判,每個(gè)events中flags代表的意義大致相同荡含。在此處 00 00 代表binlog已經(jīng)關(guān)閉, 00 01代表binlog仍開(kāi)啟.

以上便是所有event的Binlog event header的固定形式届垫,之后便不再解析释液。

Post-Header 對(duì)于每一類型的event都是相同的
FORMAT_DESCRIPTION_EVENT有Post-Header,并沒(méi)有Body部分装处,因?yàn)椴](méi)有變量误债。

04 00:binlog的版本,mysql從5.0后日志都是v4版本的妄迁。
之后50位代表mysql-server的版本寝蹈,5.7.17-log。
00 00 00 00:代表create timestamp判族,官網(wǎng)上解釋為seconds since Unix epoch when the binlog was created躺盛。
13:代表Binlog event heade的長(zhǎng)度,固定長(zhǎng)度是19形帮。

之后39位代表了39種類型事件的Post-Header的長(zhǎng)度槽惫,從0x01開(kāi)始,與上面所介紹的時(shí)間類型是對(duì)應(yīng)的辩撑。其中FORMAT_DESCRIPTION_EVENT為0x0f界斜,即長(zhǎng)度對(duì)應(yīng)5f。

image.png

詳細(xì)的關(guān)系在 https://dev.mysql.com/doc/internals/en/format-description-event.html 上有部分的對(duì)應(yīng)合冀,雖然不全但是可以對(duì)應(yīng)主要的event的Post-Header長(zhǎng)度各薇。

可以看到WRITE_ROWS_EVENT、UPDATE_ROWS_EVENT君躺、DELETE_ROWS_EVENT這三種主要event長(zhǎng)度都是0x0a峭判,因?yàn)樗麄兺瑢儆赗OWS_EVENT。

93 6c f1 77:最后四位為循環(huán)冗余校驗(yàn)碼棕叫,與解析出的CRC32 0x77f16c93是一致的林螃。注意,每一個(gè)event末尾都會(huì)有這樣四個(gè)字節(jié)俺泣。

QUERY_EVENT

image.png
SET TIMESTAMP=1521626445/*!*/;

SET @@session.pseudo_thread_id=95906/*!*/;

SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;

SET @@session.sql_mode=1437073442/*!*/;

SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;

/*!\C utf8mb4 *//*!*/;

SET @@session.character_set_client=45,@@session.collation_connection=45,@@session.collation_server=45/*!*/;

SET @@session.lc_time_names=0/*!*/;

SET @@session.collation_database=DEFAULT/*!*/;

create database abcd

/*!*/;

# at 353

#180321 18:04:24 server id 1813309  end_log_pos 418 CRC32 0x8f20b94b  GTID last_committed=1 sequence_number=2

在row格式的binlog中疗认,所有的建庫(kù)建表操作都是QUERY_EVENT。此處取出較短的建庫(kù)操作做分析伏钠,建表操作與建庫(kù)的binlog格式是完全相同的横漏。

Post-Header的長(zhǎng)度為0x0d。

00 01 76 a2:slave_proxy_id熟掂,代指一個(gè)用于臨時(shí)表的進(jìn)程號(hào)缎浇,為了避免創(chuàng)建臨時(shí)表時(shí)重復(fù)。

00 00 00 00:表示語(yǔ)句執(zhí)行時(shí)間赴肚,單位為秒华畏。

04 :代表當(dāng)前數(shù)據(jù)庫(kù)名字的長(zhǎng)度鹏秋,在這里為abcd。

00 00 :兩位錯(cuò)誤碼亡笑,若非0則代表有誤侣夷,如果從庫(kù)在復(fù)制時(shí)收到錯(cuò)誤碼將停止復(fù)制進(jìn)程。

00 21 :v4新特性仑乌,為status_vars的長(zhǎng)度百拓,這個(gè)之后會(huì)有介紹。

Body的長(zhǎng)度根據(jù)DDL語(yǔ)句的長(zhǎng)度而定晰甚。

TABLE_MAP_EVENT

image.png
SET TIMESTAMP=1521626714/*!*/;

SET @@session.time_zone='SYSTEM'/*!*/;

BEGIN

/*!*/;

# at 782

#180321 18:05:14 server id 1813309  end_log_pos 843 CRC32 0x98b87aed    Table_map: `abcd`.`test` mapped to number 224

每個(gè)DML事務(wù)之前衙传,都會(huì)有一個(gè)TABLE_MAP_EVENT,記錄操作對(duì)應(yīng)的表的信息厕九。

Post-Header的長(zhǎng)度為0x08蓖捶。

e0 00 00 00 00 00:代表庫(kù)的ID 224。

01 00 :保留位數(shù)扁远,Reserved for future use俊鱼。

Body部分:

04:庫(kù)名的長(zhǎng)度,之后是庫(kù)名的字符串61 62 63 64 00 abcd畅买,與上文相同并闲。

04:表名的長(zhǎng)度,和對(duì)應(yīng)的表名74 65 73 74 00 test谷羞。

07:代表字段的個(gè)數(shù)帝火,我們建表時(shí)設(shè)立了7種不同類型的字段。

可以看出湃缎,binlog是完全不記錄表的字段名稱的犀填,只記錄對(duì)應(yīng)的編號(hào)和類型。

讓我們重新看一下建表的內(nèi)容嗓违。

create table test (a1 int primary key not null auto_increment, a2 double not null, a3 timestamp, a4 datetime, a5 char(10), a6 varchar(4000), a7 text);

03 05 11 12 fe 0f fc 08 08 00 00 fe 28 80 3e 02 78

其中每個(gè)字節(jié)都有對(duì)應(yīng)的含義九巡。

03代表MYSQL_TYPE_LONG。

05代表MYSQL_TYPE_DOUBLE靠瞎,后面會(huì)跟一位metadata。

11代表MYSQL_TYPE_TIME求妹,這個(gè)官網(wǎng)寫(xiě)后面沒(méi)有metadata乏盐,但是其實(shí)是有一個(gè)00站位的,而且跟是否有默認(rèn)值無(wú)關(guān)制恍。

12代表MYSQL_TYPE_DATETIME父能,跟MYSQL_TYPE_TIME情況相同。

Fe代表MYSQL_TYPE_STRING净神,后面會(huì)跟兩位metadata

0f代表MYSQL_TYPE_VARCHAR何吝,后面會(huì)跟兩位metadata

Fc代表MYSQL_TYPE_BLOB溉委,后面會(huì)跟一位metadata

之后的08代表metadata length:08 00 00 fe 28 80 3e 02

首先08對(duì)應(yīng)的MYSQL_TYPE_DOUBLE的metadata,代表sizeof(double)爱榕,也就是8

之后00 00就跟上文說(shuō)的一樣瓣喊,是MYSQL_TYPE_TIME和MYSQL_TYPE_DATETIME的metadata。

fe 28:在之后的insert語(yǔ)句的binlog中黔酥,可以看到有@5='abc' /* STRING(40) meta=65064 nullable=1 is_null=0 */的內(nèi)容藻三,其中的meta值便是0xfe28。

80 3e: 與第六列中@6='abcdefghasdasdasd' /* VARSTRING(16000) meta=16000 nullable=1 is_null=0 */中的meta值一致跪者。

02:與第七列中@7='qwetrhyokxocm3479thcms9q25hdr9ker8thcfisdrhoc' /* BLOB/TEXT meta=2 nullable=1 is_null=0 */中的meta值一致棵帽。

....

上面解析binlog文件中,將其分為三個(gè)部分:通用事件頭(common-header)渣玲、私有事件頭(post-header)和事件體(event-body)逗概。本文修改了一下,只用兩個(gè)Java類來(lái)修飾binlog中的事件忘衍,即EventHeader和EventData逾苫。可以理解為下述的對(duì)應(yīng)關(guān)系:(mysql-binlog-connector就是這么做的)

EventHeader --> 通用事件頭(common-header)
EventData ---> 私有事件頭(post-header)和事件體(event-body)

于是淑履,你們可以把Binlog的文件結(jié)構(gòu)像下面這么理解


image.png

說(shuō)一下這個(gè)Checksum,在獲取event內(nèi)容的時(shí)候隶垮,會(huì)增加4個(gè)額外字節(jié)做校驗(yàn)用。mysql5.6.5以后的版本中binlog_checksum=crc32,而低版本都是binlog_checksum=none秘噪。如果不想校驗(yàn)狸吞,可以使用set命令設(shè)置set binlog_checksum=none。說(shuō)得再通俗一點(diǎn)指煎,Checksum要么為4個(gè)字節(jié)蹋偏,要么為0個(gè)字節(jié)。

下面說(shuō)一下通用事件頭的結(jié)構(gòu)至壤,如下所示


image.png

從上表可以看出威始,EventHeader固定為19個(gè)字節(jié),為此我們構(gòu)造下面的類像街,來(lái)解析這個(gè)通用事件頭

public class EventHeader {
    private long timestamp;
    private int eventType;
    private long serverId;
    private long eventLength;
    private long nextPosition;
    private int flags;
    //省略setter和getter方法
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("EventHeader");
        sb.append("{timestamp=").append(timestamp);
        sb.append(", eventType=").append(eventType);
        sb.append(", serverId=").append(serverId);
        sb.append(", eventLength=").append(eventLength);
        sb.append(", nextPosition=").append(nextPosition);
        sb.append(", flags=").append(flags);
        sb.append('}');
        return sb.toString();
    }
}

OK黎棠,接下來(lái),我們來(lái)一段代碼試著解析一下第一個(gè)事件的EventHeader,代碼如下所示

public class HeaderParser {
    
    public static final byte[] MAGIC_HEADER = new byte[]{(byte) 0xfe, (byte) 0x62, (byte) 0x69, (byte) 0x6e};
    
    public static void main(String[] args)throws Exception {
        String filePath = "D:\\mysql-bin.000001";
        File binlogFile = new File(filePath);
        ByteArrayInputStream inputStream = null;
        inputStream = new ByteArrayInputStream(new FileInputStream(binlogFile));
        byte[] magicHeader = inputStream.read(4);
        if(!Arrays.equals(MAGIC_HEADER, magicHeader)){
            throw new RuntimeException("binlog文件格式不對(duì)");
        }
        EventHeader eventHeader = new EventHeader();
        eventHeader.setTimestamp(inputStream.readLong(4) * 1000L);
        eventHeader.setEventType(inputStream.readInteger(1));
        eventHeader.setServerId(inputStream.readLong(4));
        eventHeader.setEventLength(inputStream.readLong(4));
        eventHeader.setNextPosition(inputStream.readLong(4));
        eventHeader.setFlags(inputStream.readInteger(2));       
        System.out.println(eventHeader);
        
    }
}

EventHeader{timestamp=1536487335000, eventType=15, serverId=1, eventLength=119, nextPosition=123, flags=1}

注意看镰绎,兩個(gè)參數(shù)

eventLength=119
nextPosition=123

下一個(gè)事件從123字節(jié)開(kāi)始脓斩。這是怎么算的呢,當(dāng)前事件長(zhǎng)度是是119字節(jié)畴栖,算上最開(kāi)始4個(gè)字節(jié)的魔數(shù)占位符随静,那么下一個(gè)事件自然是,119+4=123,從123字節(jié)開(kāi)始。再?gòu)?qiáng)調(diào)一次燎猛,這個(gè)119字節(jié)恋捆,是包含EventHeader,EventData,Checksum,三個(gè)部分的長(zhǎng)度為119。
最重要的一個(gè)參數(shù)

eventType=15

我們?nèi)ハ旅娴牡刂?br> https://dev.mysql.com/doc/internals/en/binlog-event-type.html
查詢一下重绷,15對(duì)應(yīng)的事件類型為FORMAT_DESCRIPTION_EVENT沸停。我們接下來(lái),需要知道FORMAT_DESCRIPTION_EVENT所對(duì)應(yīng)EventData的結(jié)構(gòu)论寨。在下面的地址https://dev.mysql.com/doc/internals/en/format-description-event.html
查詢得到EventData的結(jié)構(gòu)對(duì)應(yīng)如下表所示

image.png

ps:這個(gè)n其實(shí)我們可以推算出星立,為39。事件長(zhǎng)度為119字節(jié)葬凳,減去事件頭19字節(jié)绰垂,減去末位的4字節(jié)(末位四個(gè)字節(jié)循環(huán)校驗(yàn)碼),減去2個(gè)字節(jié)的binlog版本火焰,減去50個(gè)字節(jié)的服務(wù)器版本號(hào)劲装,減去4個(gè)字節(jié)的時(shí)間戳,減去1個(gè)字節(jié)的事件頭長(zhǎng)度昌简。得到如下算式
119?19?4?2?50?4?1=39
根據(jù)上表結(jié)構(gòu) 占业,我們給出一個(gè)JAVA類如下所示

public class FormatDescriptionEventData {
    private int binlogVersion;
    private String serverVersion;
    private long timestamp;
    private int headerLength;
    private List headerArrays = new ArrayList<Integer>();
    //省略setter和getter方法
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("FormatDescriptionEventData");
        sb.append("{binlogVersion=").append(binlogVersion);
        sb.append(", serverVersion=").append(serverVersion);
        sb.append(", timestamp=").append(timestamp);
        sb.append(", headerLength=").append(headerLength);
        sb.append(", headerArrays=").append(headerArrays);
        sb.append('}');
        return sb.toString();
    }   
}

那如何解析呢,如下所示

public class HeaderParser {

    public static final byte[] MAGIC_HEADER = new byte[] { (byte) 0xfe,
            (byte) 0x62, (byte) 0x69, (byte) 0x6e };

    public static void main(String[] args) throws Exception {
        String filePath = "D:\\mysql-bin.000001";
        File binlogFile = new File(filePath);
        ByteArrayInputStream inputStream = null;
        inputStream = new ByteArrayInputStream(new FileInputStream(binlogFile));
        byte[] magicHeader = inputStream.read(4);
        if (!Arrays.equals(MAGIC_HEADER, magicHeader)) {
            throw new RuntimeException("binlog文件格式不對(duì)");
        }
        EventHeader eventHeader = new EventHeader();
        eventHeader.setTimestamp(inputStream.readLong(4) * 1000L);
        eventHeader.setEventType(inputStream.readInteger(1));
        eventHeader.setServerId(inputStream.readLong(4));
        eventHeader.setEventLength(inputStream.readLong(4));
        eventHeader.setNextPosition(inputStream.readLong(4));
        eventHeader.setFlags(inputStream.readInteger(2));
        System.out.println(eventHeader);
        inputStream.enterBlock((int) (eventHeader.getEventLength() - 19 - 4));
        FormatDescriptionEventData descriptionEventData = new FormatDescriptionEventData();
        descriptionEventData.setBinlogVersion(inputStream.readInteger(2));
        descriptionEventData.setServerVersion(inputStream.readString(50).trim());
        descriptionEventData.setTimestamp(inputStream.readLong(4) * 1000L);
        descriptionEventData.setHeaderLength(inputStream.readInteger(1));
        int sums = inputStream.available();
        for (int i = 0; i < sums; i++) {
            descriptionEventData.getHeaderArrays().add(inputStream.readInteger(1));
        }
        System.out.println(descriptionEventData);
    }
}

binlog是主動(dòng)推還是拉

MySQL通過(guò)[向備庫(kù)傳送二進(jìn)制日志來(lái)實(shí)現(xiàn)Replication纯赎,下面將通過(guò)二進(jìn)制日志相關(guān)源代碼的主要接口來(lái)解釋:“MySQL如何傳輸二進(jìn)制日志谦疾,是主庫(kù)推,還是備庫(kù)拉犬金?MySQL日志傳輸?shù)膶?shí)時(shí)性如何念恍?”。

在MySQL Replication結(jié)構(gòu)中晚顷,備庫(kù)端初次通過(guò)CHANGE MASTER TO完成Replication配置峰伙,再使用start slave命令開(kāi)始復(fù)制。更細(xì)致的该默,備庫(kù)通過(guò)IO Thread向主庫(kù)發(fā)起讀取binlog的請(qǐng)求(COM_BINLOG_DUMP命令)瞳氓,主庫(kù)收到COM_BINLOG_DUMP請(qǐng)求后,使用單獨(dú)線程(dump thread(按照dump協(xié)議傳輸數(shù)據(jù)))不斷向備庫(kù)IO Thread發(fā)送Binlog栓袖。示意圖

image.png

在主庫(kù)端一旦有新的日志產(chǎn)生后匣摘,立刻會(huì)發(fā)送一次廣播,dump線程在收到廣播后裹刮,則會(huì)讀取二進(jìn)制日志并通過(guò)網(wǎng)絡(luò)向備庫(kù)傳輸日志音榜,所以這是一個(gè)主庫(kù)向備庫(kù)不斷推送的過(guò)程;

新日志在產(chǎn)生后必指,只需一次廣播和網(wǎng)絡(luò)就會(huì)立刻(<1ms)向發(fā)送到備庫(kù)囊咏,如果主備之間網(wǎng)絡(luò)較好的話(例如RTT<1ms)恕洲,備庫(kù)端的日志也就小于2ms了塔橡。所以梅割,一般的(依賴于RTT),備庫(kù)的實(shí)時(shí)性都非常好葛家。

到底是主庫(kù)推送户辞,還是備庫(kù)請(qǐng)求的方式?這也是我之前困惑的問(wèn)題癞谒。所以后來(lái)通過(guò)分析源碼底燎,發(fā)現(xiàn):一旦連接建立,則是由主庫(kù)主動(dòng)推送的弹砚。(從mysql-binlog-connector源碼debug也可以得到該結(jié)論)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末双仍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子桌吃,更是在濱河造成了極大的恐慌朱沃,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茅诱,死亡現(xiàn)場(chǎng)離奇詭異逗物,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)瑟俭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)翎卓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人摆寄,你說(shuō)我怎么就攤上這事失暴。” “怎么了椭迎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵锐帜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我畜号,道長(zhǎng)缴阎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任简软,我火速辦了婚禮蛮拔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痹升。我一直安慰自己建炫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布疼蛾。 她就那樣靜靜地躺著肛跌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衍慎,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天转唉,我揣著相機(jī)與錄音,去河邊找鬼稳捆。 笑死赠法,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乔夯。 我是一名探鬼主播砖织,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼末荐!你這毒婦竟也來(lái)了侧纯?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤甲脏,失蹤者是張志新(化名)和其女友劉穎茂蚓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體剃幌,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡聋涨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了负乡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牍白。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖抖棘,靈堂內(nèi)的尸體忽然破棺而出茂腥,到底是詐尸還是另有隱情,我是刑警寧澤切省,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布最岗,位于F島的核電站,受9級(jí)特大地震影響朝捆,放射性物質(zhì)發(fā)生泄漏般渡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一芙盘、第九天 我趴在偏房一處隱蔽的房頂上張望驯用。 院中可真熱鬧,春花似錦儒老、人聲如沸蝴乔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)薇正。三九已至片酝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挖腰,已是汗流浹背钠怯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留曙聂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓鞠鲜,卻偏偏與公主長(zhǎng)得像宁脊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贤姆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361