MySQL Binlog GTID

事务提交时更新`executed_gtids`内存集合,在`Binlog`文件rotate和服务器关闭时将`executed_gtids`持久化到`mysql.gtid_executed`表中。在MySQL 8.0.17中,为了支持克隆功能,事务GTID被写入InnoDB的undo日志中,并维护了一个后台线程`clone_gtid_thread`,周期性持久化事务GTID到`gtid_executed`表。un...
MySQL Binlog GTID
GTID标识了Binlog事务的全局唯一性,确保每个事务在集群的每个实例中执行一次。开启Binlog与GTID后,MySQL为每个事务绑定一个GTID。在执行成功后,相关GTID被记录在Binlog中。这使得MySQL能通过GTID的状态判断状态机状态,从而在搭建复制时,通过GTID判断主从复制节点的事务执行状态。上游节点可以将下游节点尚未执行的事务同步至下游。

开启GTID需要设置`enforce_gtid_consistency=ON`与`gtid_mode=ON`。`enforce_gtid_consistency`保证了以GTID事务形式安全记录语句,确保所有操作都以原子性执行。同时,`gtid_mode`还有`ON_PERMISSIVE`、`OFF_PERMISSIVE`、`OFF`等参数,描述了主从节点产生的Binlog事务是否记录GTID。

在复制上下游时,通过GTID可以实现主节点将自己的GTID集合发送给从节点,从节点据此同步数据。使用GTID复制时,从节点必须设置`gtid_mode`为`ON`,主节点则保持`gtid_mode`非`OFF`状态,并在`change master`语句中设置`MASTER_AUTO_POSITION=1`。此时,从节点将自己的`gtid_executed`发送给主节点,主节点据此计算出需要同步的Binlog起始位置。

为了确保主从状态一致,从节点的`gtid_executed`集合必须为主节点`gtid_executed`和正在执行事务GTID的子集。若从节点的`gtid_executed`包含主节点没有的GTID,可以通过设置主节点的`gtid_purged`全局变量,或使用`reset master`命令重置从节点的GTID状态来解决。

GTID的产生有两种方式:系统生成和用户自定义。在事务提交时,为事务生成一个GTID,该过程涉及`generate_automatic_gtid`函数,GTID格式为`server_uuid:gno`,其中`server_uuid`由`show global variables like "%uuid%"`查看,`gno`是顺序递增的数字。系统将生成的GTID存储在`gtid_owend`集合中,直到事务提交后,GTID从`gtid_owend`中移除并加入`gtid_executed`集合。如果事务回滚,相应的GTID也会从`gtid_owend`中删除。

相关函数包括`generate_automatic_gtid`和`update_gtids_impl_own_gtid`。可以通过`SET GTID_NEXT`语句在事务开启前设定GTID。需要注意的是,如果已经为事务设置了GTID,不应再次设置。

GTID在内存中主要以`gtid_executed`、`gtid_purged`、`gtid_owend`、`gtid_submitted`和`lost_gtids`五个集合的形式存在。其中,`gtid_executed`记录所有已执行的Binlog事务,通过`@@GLOBAL.gtid_executed`查看。`gtid_purged`记录所有执行但不在Binlog中的事务,是`gtid_executed`的子集。`gtid_owend`记录处于生成和提交过程之间的GTID。`gtid_submitted`记录最后一个Binlog中的`previous_gtids`事件的GTID集合。`lost_gtids`记录不在binlog中但存在于`gtid_executed`表中的GTID。

`Gtid_set`类是GTID集合的内存数据结构,包含三个重要成员:`m_intervals`、`sid_map`和`free_intervals`。`m_intervals`是一个数组,每个slot挂了一个interval链表,每个interval存储连续的gno区间。`sid_map`记录`server_uuid`与`sidno`的映射关系,`sid`是`server_uuid`的别名,`sidno`是`m_intervals`数组槽位下标的加一。MySQL保存了一份全局的`sid_map`(`global_sid_map`),不同`Gtid_set`共享这个映射,以便快速查找并访问gno。

持久化方面,在MySQL 5.7.5之前,GTID不会持久化到表中,而是每次从Binlog构建。为了解决某些情况下需要使用GTID的场景,MySQL将`gtid_executed`集合持久化保存在`gtid_executed`表中,其他GTID内存集合如`gtid_purged`等无需持久化,因为即使Binlog丢失,仍可通过`gtid_executed`计算得到。

`gtid_executed`表和Binlog中的GTID存储方式如下:Binlog中的`previous_gtids`事件记录了前一个Binlog的`Previous_gtids`事件中的GTID集合加上前一个Binlog中所有事务的GTID。递归进行,最后一个Binlog的`Previous_gtids`事件记录了所有Binlog中的GTID,包括已被purge的Binlog。

事务提交时更新`executed_gtids`内存集合,在`Binlog`文件rotate和服务器关闭时将`executed_gtids`持久化到`mysql.gtid_executed`表中。在MySQL 8.0.17中,为了支持克隆功能,事务GTID被写入InnoDB的undo日志中,并维护了一个后台线程`clone_gtid_thread`,周期性持久化事务GTID到`gtid_executed`表。undo的purge也会受到该线程的限制,未持久化的GTID不会被purge。有了这个功能,即使主服务器的Binlog丢失,只要从服务器具有`gtid_executed`表和undo日志,就能构建出`executed_gtids`结构,继续从主服务器拉取Binlog。在MySQL 8.0.17之前的版本中,使用xtrabackup重建备库时,也需要复制主服务器的`gtid_executed`信息以重新建立Binlog连接。

在主服务器启动时,初始化`executed_gtids`等集合。通过从`mysql.gtid_executed`表中读取持久化的GTID和最后一个`binlog`中记录的GTID一起构建`executed_gtids`内存结构。在此之前,会进行`binlog`和`innodb`的恢复,删除未提交的`Binlog`事务并创建新的`binlog`文件。最后一个`binlog`文件指的是服务器崩溃时的最后一个`binlog`文件。随后,也会构建`lost_gtids`、`previous_gtids_logged`和`gtids_only_in_table`这些集合,并构建`prev_gtids_ev`并写入恢复出的新`binlog`文件。

对于MySQL 8.0.17,`innodb recovery`时从undo中构建`executed_gtids`。这一过程详细见函数`trx_undo_gtid_read_and_persist`。

参考文档包括:MySQL的引擎特性、基于GTID复制实现的工作原理、GTID格式与存储等。2024-10-26
mengvlog 阅读 7 次 更新于 2025-07-20 15:59:43 我来答关注问题0
檬味博客在线解答立即免费咨询

mySQL相关话题

Copyright © 2023 WWW.MENGVLOG.COM - 檬味博客
返回顶部