导读
binlog中事务以GTID_EVENT开始, 以XID_EVENT结束, 很多信息都藏在gtid_event中, 比如事务大小, 提交时间等. MTS也会查看gtid中的信息, 还涉及到两阶段提交, 但不是本文的重点, 就不讲了.
注:
-
immediate_ 开头的表示是当前数据库执行的相关信息
-
original_ 开头的表示是主库数据库执行的信息
-
5.7同步到8.0的时候, original_ 开头的信息会被置为0
GTID_LOG_EVENT
gtid_event是一个事务开始的标记.(虽然是提交的时候才获取的…). 主要分为gtid(GTID_LOG_EVENT= 33)和匿名gtid(ANONYMOUS_GTID_LOG_EVENT= 34). 格式是一样的. 这里就只看gtid了
| 对象 | 大小(字节) | 描述 |
|---|---|---|
| GTID_FLAGS | 1 | |
| SID | 16 | server_uuid |
| GNO | 8 | gtid no. 就是看到的那个数字 |
| lt_type | 1 | logical timestamp flag |
| last_committed | 8 if lt_type == 2 else 0 | 上一个事务提交的编号, 一样的话, 就表示是一组的(就可以并行回放) |
| sequence_number | 8 if lt_type == 2 else 0 | 当前事务的编号 |
| immediate_commit_timestamp | 8 | 当前提交时间(产生这个binlog的时间) |
| original_commit_timestamp | 0/8 | 主库的提交时间(原始数据的提交时间) |
| transaction_length | 45300 | 事务大小(含gtid&xid) |
| immediate_server_version | 4 | 当前数据库的版本(产生这个Binlog的库的版本信息) |
| original_server_version | 0/4 | 主库的版本信息(原始数据的数据库版本信息) |
没错, gtid_event到这里就完了. 包含的信息还是很多的.
original_ 计算方式
original_commit_timestamp 的计算方式为: immediate_commit_timestamp 第一bit为1时, 就有immediate_commit_timestamp
否则 immediate_commit_timestamp 就是 immediate_commit_timestamp
说白了, 就是从库回放的时候, 把主库的immediate_commit_timestamp写为original_commit_timestamp , 然后把自己的immediate_commit_timestamp 加了个(1<<55)
original_server_version 也是同理的. 看起来表示得优点复杂, 直接看代码吧, 好理解点.
ENCODED_COMMIT_TIMESTAMP_LENGTH = 55
ENCODED_SERVER_VERSION_LENGTH = 31
self.immediate_commit_timestamp = self.read_uint(7)
if (self.immediate_commit_timestamp & (1<<ENCODED_COMMIT_TIMESTAMP_LENGTH))!=0:
self.immediate_commit_timestamp &= ~(1<<ENCODED_COMMIT_TIMESTAMP_LENGTH)
self.original_commit_timestamp = self.read_uint(7)
else:
self.original_commit_timestamp = self.immediate_commit_timestamp
self.immediate_server_version = self.read_uint(4)
if (self.immediate_server_version & (1<<ENCODED_SERVER_VERSION_LENGTH)) != 0:
self.immediate_server_version &= ~(1<<ENCODED_SERVER_VERSION_LENGTH)
self.original_server_version = self.read_uint(4)
else:
self.original_server_version = self.immediate_server_version
transaction_length计算方式
@mysys/pack.cc net_field_length_size
第一字节表示大小, < 251, 就直接返回, 251的话, 就read 1 . 直接看代码吧, 好理解点
data = self.read_uint(1)
if data < 251:
return data
elif data == 251:
return self.read_uint(1)
elif data == 252:
return self.read_uint(2)
elif data == 253:
return self.read_uint(3)
else:
return self.read_uint(8)
验证
光说这么多, 没用, 我们实际来验证下吧, 和官方的mysqlbinlog工具比较一下.
我们的工具解析出来信息如下:

这是官方解析出来的结果

| 对象 | 我们解析出来的gtid | mysqlbinlog解析出来的 | 是否一致 |
|---|---|---|---|
| immediate_commit_timestamp | 1713507726927132 | 1713507726927132 | 一致 |
| original_commit_timestamp | 0 | 0 | 一致 |
| sequence_number | 1 | 1 | 一致 |
| last_committed | 0 | 0 | 一致 |
| transaction_length | 291 | 291 | 一致 |
| server_uuid | 6d650f1f-ba4e-11ed-99ab-000c2980c11e | 6d650f1f-ba4e-11ed-99ab-000c2980c11e | 一致 |
| GTID | 300071 | 300071 | 一致 |
解析结果和官方完全一致, 说明解析正确.
original_commit_timestamp 为0 的原因是因为mysql版本问题. 开头已经提过了.
PREVIOUS_GTIDS_LOG_EVENT
PREVIOUS_GTIDS_LOG_EVENT 是在binlog开头的, 用来记录已经执行过的了gtid信息.
格式如下:
| 对象 | 大小 | 描述 |
| gtid_number | 8 | gtid数量 |
| gtid_list | gtid_number*gtid_info | 已经执行过的gtid详情 |
gtid_info
| 对象 | 大小 | 描述 |
| server_uuid | 16 | server uuid |
| group_gno_number | 8 | 多少对gno |
| start_gno | 8 | 每对的起始位置 |
| stop_gno | 8 | 每对的结束位置 |
直接解析对比下吧
官方的mysqlbinlog

我们解析的binlog

一样的.
这里就不放源码了. 因为 pymysqlbinlog 还没写完.




