A的binlog在B应用时,B会通过 Executed_Gtid_Set 来记录A的binlog在B的执行情况。而A的binlog中gtid是连续的,从而

  1. B未开启并行复制,B依次应用binlog,Executed_Gtid_Set中B的gtid集合应该是连续的,如;

  2. B开启并行复制此时,B并发应用binlog,Executed_Gtid_Set中B的gtid集合末端可能会出现不连续的情况,如A:1-92:94-96:98-100, 但是如果在A停止写入,B和A完全同步的情况下,Executed_Gtid_Set中B的gtid集合应该是连续的。

以下分析均基于双主结构。

并行复制

前面提到,并行复制时,Executed_Gtid_Set 尾部可能出现正常的空洞。这种不一致可以忽略,最终会一致的。

change master导致gtid丢失

change master重新指定binlog的拉取位置会导致主备gtid不一致。

考虑如下情况

主库:

  1. drop view v1;

主库:

执行失败但仍然会生成binlog, binlog中error_code=1050,参考之前月报binlog event 中的 error code

  1. SET TIMESTAMP=1453109591/*!*/;
  2. CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` AS select * from t1

备库:

备库create view执行成功,而与预期应出现的binlog error_code不一致导致备库复制中断。

那么如何修复此类错误呢?首先想到这个binlog事件是可以跳过的,因此尝试使用跳过空事务的方式来尝试修复。实际上,这种修复方式是不起作用的。

构造空事务来忽略相同gtid,执行start slave;后SQL线程执行过程如下

  1. 首先预检查gtid是否执行,如果此gtid已执行,语句直接返回成功0;

因此,此类错误通过构造空事务方式无法修复。

此时就需要change master 方式指向失败事件的下一个位点。然后按位点的方式(master_auto_position=0)来拉binlog。

  1. stop slave;
  2. start slave;

至此失败已经修复,但show slave status可以看到 Executed_Gtid_Set 存在空洞,gtid已经不一致了。

此时,我们可以通过skip空事务的方式来弥补这个空洞。

主机crash

主库所在主机crash后,可能导致主库比备库少一些gtid。

binlog在写文件时先write,再sync。假设主库在write binlog之后,sync 之前,同时备库也拉取了这些未sync的binlog。此时主库宕机,主库一部分 binlog 未落盘,但这部分binlog已经传到了备库,那么备库会比主库多一些事务。因此主库重启后,重新构造 gtid_executed_set 时会比备库少一些gtid。

那些未sync的事务实际处于两阶段提交的prepare状态,重启后这些处于prepare的事务由于没有写binlog会回滚掉。

主机宕机HA切换后,新主库会比新备库多一些事务。

而实际上新主库会比新备库多一些事务应该没有影响,这些事务是用户发出了commit命令,但主机crash了,没有收到commit的回复,处于未知状态。这些未决事务可以提交也可以回滚!

对于以上情况,在binlog没有purge的情况下,结合应用我们可以根据gtid来修复主备不一致的情况,或回滚备库的修改,或者重做主库丢失的事务。

假设备库比主库少一些gtid, 而主库多出来的这些gtid已经purge了。如果用备库做的备份集来恢复出一个实例时,备库会去主库拉取缺少的那些gtid,而那些gtid已经purge了。 这就会导致臭名昭著的1236问题

  1. Last_IO_Errno: 1236
  2. Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.'

根据前面的gtid不一致的分析,我们应该提前发现不一致,并及时修复,避免此类情况发生。

这里总结些gtid不一致的修复方法:

  1. 修改GTID_PURGED的方式 先reset master,再设置GTID_PURGED来修复不一致,此种方式需确保主备数据一致的情况下进行,风险较大,酌情考虑。