暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

[pymysqlbinlog] 解析binlog中的gtid (GTID_LOG_EVENT,PREVIOUS_GTIDS_LOG_EVENT)

原创 大大刺猬 2024-04-19
913

导读

binlog中事务以GTID_EVENT开始, 以XID_EVENT结束, 很多信息都藏在gtid_event中, 比如事务大小, 提交时间等. MTS也会查看gtid中的信息, 还涉及到两阶段提交, 但不是本文的重点, 就不讲了.

注:

  1. immediate_ 开头的表示是当前数据库执行的相关信息

  2. original_ 开头的表示是主库数据库执行的信息

  3. 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工具比较一下.

我们的工具解析出来信息如下:
image.png
这是官方解析出来的结果
image.png

对象 我们解析出来的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
image.png
我们解析的binlog
image.png
一样的.

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

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论