背景

在One Master / Multi Slaves的场景下,
对于Master/Slave的binlog存在如下四种情况:
【1】Master binlog单方面增加
【2】Master binlog单方面删除
【3】Slave binlog单方面增加
【4】Slave binlog单方面删除
对于复制的binlog执行过程,还存在两种情况:
【5】事务执行成功
【6】事务执行失败,原因可能是:

  • Master/Slave初始数据不一致,相同的事务在Slave上执行报错

其中:

  • 对于【1】:Slave通过常规复制完成数据同步
  • 对于【2】:【ERROR 1263】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. ‘
  • 对于【3】Errant Transaction
  • 对于【4】:常规binlog删除操作
  • 对于【5】:执行成功
  • 对于【6】:报错的依赖于具体的SQL语句

以下重点讨论【2】【3】【6】

情况2:ERROR 1263

Master单方面删除binlog是指:

  • Master删除了一部分binlog
  • Slave还没有复制这部分binlog
  • Slave请求这部分binlog,报错

【例如】
【分析】
从注释中可以看出,产生这个错误的原因是Master的lost_gitds不是slave_gtid_executed的子集,也就是Master删除了Slave没有执行的GTIDs
【解决方法1】FILE & POS

  • STOP SLAVE
  • CHANGE MASTER TO … FILE & POS , MASTER_AUTO_POSITION = 0
  • START SLAVE

当START SLAVE、正常数据同步后,很可能会产生GTID空洞,GTID(三):不连续的GTID
【解决方法2】重置GTID_PURGED
— Master

  • select @@gtid_purged;

— Slave

  • STOP SLAVE
  • RESET MASTER
  • SET GLOBAL GTID_PURGED=’master_uuid:start-end’
  • START SLAVE

思想为重置slave_gtid_executed=Master lost_gitds:

  • 因为gtid_executed为只读变量,无法直接设置
  • 但当gtid_executed为空时,可以通过设置gtid_purged来设置gtid_executed
  • RESET MASTER可以使gtid_executed为空

【注意】此时的Slave不会再执行Master上的GTID在master_uuid:start-end中的事务了,要保证所有数据均已同步,可以采用【解决方法1】来保证,然后再使用【解决方法2】来再次启用GTID复制模式
源码如下:

情况3:Errant Transaction

【可能原因1】Slave违规写入
正常的,单独在Slave写入违反Master/Slave结构的模式(Write-To-Master,Read-From-Slave)
如果发生单独在Slave写入事务,该事务称为Errant Transaction,在Slave升级为Master后可能导致Errant Transaction传递下去,考虑如下场景:

  • Node1为Master,Node2为Slave,在GTID模式下,Node2执行了一个Errant Transaction,记作T1
  • Node1崩溃后,Node2变为新的Master,Node3为新的Slave
  • Node3会从Node2复制T1,并执行T1
  • Errant Transaction并不是客户端/用户执行的事务(客户端/用户执行的事务都由Node1执行,并传递给Node2等所有的Slave)

此时需要跳过这个Errant Transaction,设Errant Transaction的GTID为ET_GTID
如果需要在Slave上写入,建议SET SQL_LOG_BIN=0(事务不写入binlog)
【可能原因2】Master Crash
MySQL 5.6中Master上的事务在提交时,事务被写入binlog并将binlog刷盘,并同时发送给Slave,写入binlog和发给Slave同时进行. Master在将binlog刷盘后,将事务提交(Commit)到InnoDB,然后开始
等待Slave的ACK,在收到Slave的ACK之后,然后将结果返回到提交事务的Client,那么如果Master Commit InnoDB失败,而Slave上已经提交,会导致Slave上比Master有更多的GTIDs
【解决方法1】空事务
在Slave上执行序号为ET_GTID的空事务,这样Slave就不会再执行Master复制过来的相同序号的Errant Transaction

  • STOP SLAVE
  • SET GTID_NEXT=’ET_GTID’
  • BEGIN;COMMIT;
  • SET GTID_NEXT=”AUTOMATIC”;
  • START SLAVE;

【解决方法2】重置GTID_PURGED
同【情况2】的【解决方法2】重置GTID_PURGED

情况6:事务执行报错

由于数据为完全同步的原因思想为在Slave上跳过报错的事务,设报错的事务GTID为Err_GTID
【解决方法】空事务
同【情况3】的【解决方法1】空事务