前言
这是一篇旧文,之前做项目时遇到的问题,通过查阅相关资料和代码整理而来。本文不会介绍GTID的用法及配置,相关资料网上有一大堆,可自行查阅,主要是为了说明MySQL的GTID在5.6和5.7两个大版本之间的差异,这方面的介绍除了官方文档中有一些介绍外,其他资料到还不多。希望可以让你对GTID的了解更深入一些。
两个版本的GTID差异
配置要求不同
开启GTID复制时,一般涉及这4个配置:
- gtid_mode=ON
- log_bin=ON
- log-slave-updates=ON
- enforce-gtid-consistency
它们的作用分别表示:是否开启GTID模式、是否开启Binlog文件、slave日志中是否记录SQL线程的更新、是否拒绝可能导致GTID不一致的SQL语句;
在mysql5.6中,如果要使用GTID复制,这四个配置必须全都要启用;但是在mysql5.7中,在使用GTID时,log_bin
和log-slave-updates
两个配置不再是强制开启的(Why?)。不过一般情况下,为了做高可用切换,我们还是会默认开启log_bin
和log-slave-updates
。
GTID保存逻辑不同
使用mysql时,假设我们开启了GTID,那么我们查看GTID的方式可以为:
show master status
show slave status
select @@global.gtid_executed
在mysql 5.6中,Executed_Gtid_Set
与global.gtid_executed
的取值是相同的,他们都来自于binlog中GTID的集合。具体过程是这样的:
- 每次启动的时候:
- 首先,从最新binlog中读取
Previous_gtids_log_event
的gtid列表,加入logged_gtids集合中; - 其次,从最新binlog中读取所有的
Gtid_log_event
的gtid,加入到logged_gtids集合中;
- 首先,从最新binlog中读取
- 启动之后,后续每个执行的事务所生成的gtid也都会更新到logged_gtids集合中;
总结就是:在MySQL5.6中,Executed_Gtid_Set
是在mysqld启动时从最新的binlog中加载的。现在回头来看开启GTID的时候为什么要开启log_bin参数?因为Executed_Gtid_Set
是保存在binlog中的,启动的实话需要加载GTID信息,所以必须要开启binlog。
这里再对照mysql源码看下上面的说明。
show master status
select @@global.gtid_executed
因此,如果启动的时候binlog被清空了,则Executed_Gtid_Set显示为空,后续执行的事务会从事务编号为1的GTID重新开始。读者可自行验证。
下面我们再看下在mysql 5.7中的行为。
首先,5.7中引入了一个新的系统表mysql.gtid_executed
, 用于保存已经执行的事务的GTID集合。具体的逻辑为:
- 当binlog未开启时,每个执行事务的GTID都会保存在该表中。此时,会启用一个新的GTID压缩线程对该表进行定时压缩,压缩的频率可以通过参数
gtid_executed_compression_period
来控制(该参数意思是每执行gtid_executed_compression_period
个事务执行一次压缩);当使用主从复制时,这种情况只能用于slave上,master上是必须要开启binlog的; - 当binlog开启时,每当binlog轮转或者server正常shutdown时,上一个binlog中的所有GTID集合(包括
Previous_gtids_log_event
)会记录到该表中;但是,当server非正常关闭时,当前binlog中的GTID并未保存到mysql.gtid_executed
表中,在server重启执行recovery时会将这部分GTID保存到mysql.gtid_executed
表中;同时注意,开启binlog时,不会启用binlog压缩,参数gtid_executed_compression_period
不起作用;
其次,Executed_Gtid_Set
变量的值以及global.gtid_executed
的值,不再从binlog中读取,而是从系统表mysql.gtid_executed中获取,见下图mysql5.7.18源码实现:
show master status
select @@global.gtid_executed
- 函数
get_executed_gtids()
,注意函数注释
因此,在mysql 5.7中,开启GTID不再强制要求开启binlog,只有当需要做主从复制中的master时,才必须开启binlog(为了保证高可用时的主从切换,还是建议同时开启主从上的binlog)。但是,如果非正常shutdown后,在启动的时候binlog被清空了,5.7中的Executed_Gtid_Set
不会显示为空,而是最后一个binlog的Previous_gtids
,相比5.6版本,这缺失的不是全部的GTID,而是最后一个binlog中的gtid_event
。
总结
总体而言,相比5.6版本,5.7版本对GTID的改进,减少了GTID的使用限制,同时也加强了GTID信息的持久化。不知道在8.0版本中,在GTID方面又有哪些变化,还没来得及看。
参考
- https://dev.mysql.com/doc/refman/5.6/en/replication-gtids-howto.html
- https://www.percona.com/sites/default/files/presentations/mysql_56_GTID_in_a_nutshell.pdf
- https://dev.mysql.com/doc/refman/5.6/en/replication-options-gtids.html#sysvar_enforce_gtid_consistency
- https://dev.mysql.com/doc/refman/5.6/en/replication-options-gtids.html#sysvar_gtid_executed
- https://dev.mysql.com/doc/refman/5.7/en/replication-gtids-howto.html
- https://dev.mysql.com/doc/refman/5.7/en/replication-gtids-concepts.html#replication-gtids-gtid-executed-table