MySQL Replication--全局参数gtid_executed和gtid_purged
参数定义
gtid_executed,全局参数,GTID集合包含所有在该服务器上执行过的事务编号和使用set gtid_purged语句设置过的事务编号,使用SHOW MASTER STATUS和SHOW SLAVE STATUS命令得到的Executed_Gtid_Set列值就取自于全局参数gitd_executed。
gtid_purged,全局参数,GTID集合包含从binlog中purged掉的事务ID,该集合是全局参数gtid_executed的子集。
官方参数解释:
gtid_executed
When used with global scope, this variable contains a representation of the set of all transactions executed on the server and GTIDs that have been set by a SET gtid_purged statement. This is the same as the value of the Executed_Gtid_Set column in the output of SHOW MASTER STATUS and SHOW SLAVE STATUS.
gtid_purged
The set of all transactions that have been purged from the binary log. This is a subset of the set of transactions in gtid_executed.
参数更新机制
当复制主库关闭binlog时:
1. 事务提交不会生成GTID,mysql.gtid_executed表/gtid_executed变量/gtid_purged变量均不更新。
当复制主库开启binlog时:
1. 事务提交需要生成Binlog,GTID在Binlog的ordered_commit flush阶段生成。
2. 表mysql.gtid_executed在实例重启或flush log或binlog文件写满等造成binlog发生切换是保存上一个binlog执行过的全部gtid,属于非实时更新。
3. 全局变量gtid_executed在事务commit阶段更新,属于实时更新。
4. 全局变量gtid_purged在执行purge binary log命令或binlog超过保持期进行清理binlog时更新,属于非实时更新。
当复制从库关闭binlog或关闭log_slave_update时:
1. 在从库上应用主库binlog时不会生成新的GTID,也不会写入复制从库的binlog文件。
2. 表mysql.gtid_executed在应用主库binlog事务时更新,并与事务一起提交,属于实时更新。
3. 全局变量gtid_executed在主库binlog事务commit阶段更新,属于实时更新。
4. 全局变量gtid_purged在主库binlog事务commit阶段更新,属于实时更新。
当复制从库开启binlog或开启log_slave_update时:
1. 在从库上应用主库binlog时不会生成新的GTID,但会写入复制从库的binlog文件。
2. 表mysql.gtid_executed在实例重启或flush log或binlog文件写满等造成binlog发生切换是保存上一个binlog执行过的全部gtid,属于非实时更新。
3. 全局变量gtid_executed在事务commit阶段更新,属于实时更新。
4. 全局变量gtid_purged在执行purge binary log命令或binlog超过保持期进行清理binlog时更新,属于非实时更新。
参数重置机制
在某些场景下,需要修改全局变量gtid_purged和gtid_executed的值,执行对全局变量gtid_purged进行赋值时,会报以下错误:
ERROR 1840 (HY000): @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.
设置@@GLOBAL.GTID_EXECUTED需要通过RESET MASTER命令,该命令会情况当前服务器上的Binlog文件,并将@@GLOBAL.GTID_EXECUTED和@@GLOBAL.GTID_PURGED的值重置为空,重新初始化binlog文件序号,重新初始化GTID的事务ID起始值.
参数恢复机制
参数binlog_gtid_simple_recovery用于控制在实例重启时,如何计算全局变量gtid_executed和gtid_purged。在MySQL 5.7.7及之后版本中该参数默认为True
binlog_gtid_simple_recovery=FALSE
• To initialize gtid_executed, binary log files are iterated from the newest file, stopping at the first binary log that has any Previous_gtids_log_event. All GTIDs from Previous_gtids_log_event and Gtid_log_events are read from this binary log file. This GTID set is stored internally and called gtids_in_binlog. The value of gtid_executed is computed as the union of this set and the GTIDs stored in the mysql.gtid_executed table.
This process could take a long time if you had a large number of binary log files without GTID events, for example created when gtid_mode=OFF.
• To initialize gtid_purged, binary log files are iterated from the oldest to the newest, stopping at the first binary log that contains either a Previous_gtids_log_event that is non-empty (that has at least one GTID) or that has at least one Gtid_log_event. From this binary log it reads Previous_gtids_log_event. This GTID set is subtracted from gtids_in_binlog and the result stored in the internal variable gtids_in_binlog_not_purged. The value of gtid_purged is initialized to the value of gtid_executed, minus gtids_in_binlog_not_purged.
binlog_gtid_simple_recovery=TRUE
which is the default in MySQL 5.7.7 and later, the server iterates only the oldest and the newest binary log files and the values of gtid_purged and gtid_executed are computed based only on Previous_gtids_log_event or Gtid_log_event found in these files. This ensures only two binary log files are iterated during server restart or when binary logs are being purged.
在实例重启后,全局变量gtid_executed和gtid_purged的值取决于:
1、从binlog文件头读取Previous_gtids_log_event获取该binlog之前的gtid_set
2、从binlog文件的所有事件中获取该binlog中包含的gtid_set
3、从mysql.gtid_executed表获取到执行过的gtid_set
通过上面三个集合算出当前实例执行过的gtid_set和当前BINLOG中包含的gtid_set。
mysql.gtid_executed表更新机制
在MySQL 5.6版本中,开启GTID模式必须打开参数log_slave_updates,即从库必须在记录一份binlog,以保证在从库重启后,可以通过扫描最后一个二进制日志获得当前从库执行过的GTID集合。
在MySQL 5.7.5版本中引入mysql.gtid_executed表来存放gtid信息,因此在GTID模式下不要求开启log_slave_update参数。
在MySQL 5.7版本中,对mysql.gtid_executed表的更新策略分为:
1、主库服务器,未开启log_bin参数,则不对mysql.gtid_executed表进行更新。
2、主库服务器,开启log_bin参数,当二进制文件进行rotation时或者关闭实例时对mysql.gtid_executed进行更新。
3、从库服务器,未开启log_bin参数或未开启log_slave_updates参数,在用户事务执行时对mysql.gtid_executed进行更新,并与用户事务一起进行提交。
4、从库服务器,开启log_bin参数和开启log_slave_updates参数,当二进制文件进行rotation时或者关闭实例时对mysql.gtid_executed进行更新。
当主库开启log_bin参数或从库开启log_slave_update参数时,执行或应用事务所生成的binlog会写入磁盘,当二进制文件进行rotation时或者关闭实例时对mysql.gtid_executed进行更新,表mysql.gtid_executed中不能提供完整的GTID集合数据,需以参数gtid_executed为准,如果实例异常重启,最近的GTID集合数据没有更新到mysql.gtid_executed表中,当实例恢复时,通过扫描最后一个binlog文件来获得最新的GTID集合数据。
当从库未开启log_bin参数或未开启log_slave_updates参数时,应用主库所传递过来的事务不会产生新的binlog,在执行事务开始便可以获取到该事务的GTID,因此可以随着事务一起提交,类似于MySQL 5.6版本中随事务一起更新mysql.slave_master_info表一样。
当执行RESET MASTER时,表mysql.gtid_executed会被清空。
mysql.gtid_executed表压缩
当从库未开启log_slave_updates参数时,由于每个事务都会向mysql.gtid_executed表写入记录,为防止mysql.gtid_executed表数据量暴增,MySQL 5.7引入参数gtid_executed_compression_period来控制每执行N个事务后,对mysql.gtid_executed表进行一次压缩,将多个连续的gtid值合并为gtid集合。如果gtid_executed_compression_period被设置为0,则不会进行压缩。
当主库开启log_bin参数或从库开启log_slave_update参数时,参数gtid_executed_compression_period并不会被使用,只有当二进制文件进行Rotation时才会进行GTID压缩。
Xtrabackup备份恢复
在使用Xtrabackup热备+BINLOG位点方式搭建主从复制的场景中,会发现从库上存在GTID空隙。
由于所有数据库服务器都配置开启log_bin参数和log_slave_updates参数,因此每次执行事务不会更新mysql.gtid_executed表。
在使用Xtrabackup热备搭建主从复制时,假设:
1、T1时间点开始热备。
2、T2时间点备份到mysql.gtid_executed表。
3、T3时间点mysql.gtid_executed表最后一次更新。
4、T4时间点热备完成
在使用Xtrabackup热备恢复后,新实例上mysql.gtid_executed表为T3时间点的gtid_set,而新实例上没有任何BINLOG文件,因此新实例上全局参数gtid_executed和gtid_purged的值为T3时间点的gtid_set。而使用基于位点的方式搭建复制,新从库从T4时间点后的BINLOG日志开始应用。最终导致T3时间点到T4时间点的gtid_set没有包含在新从库的全局参数gtid_executed中,出现GTID空隙。
使用Xtrabackup备份过程中,会执行以下FLUSH操作:
2019-07-18T18:17:21.025361+08:00 37 Query SET SESSION lock_wait_timeout=31536000
2019-07-18T18:17:21.025503+08:00 37 Query FLUSH NO_WRITE_TO_BINLOG TABLES
2019-07-18T18:17:21.025841+08:00 37 Query FLUSH TABLES WITH READ LOCK
2019-07-18T18:17:21.069165+08:00 37 Query SHOW VARIABLES
2019-07-18T18:17:21.072180+08:00 37 Query SHOW SLAVE STATUS
2019-07-18T18:17:21.072291+08:00 37 Query SHOW MASTER STATUS
2019-07-18T18:17:21.072340+08:00 37 Query SHOW VARIABLES
2019-07-18T18:17:21.074537+08:00 37 Query FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS
2019-07-18T18:17:21.275242+08:00 37 Query UNLOCK TABLES
2019-07-18T18:17:21.275749+08:00 37 Query SELECT UUID()
2019-07-18T18:17:21.275851+08:00 37 Query SELECT VERSION()
上面三次FLUSH 操作都不会导致二进制文件进行rotation,因此也不会导致更新mysql.gtid_executed表数据。