1. 普通的master-slave 异步replication
  2. 支持多通道的group replication和double binlog

如果按连接协议来区分,又可以分为

  1. 非GTID模式,通过binlog文件名和文件的偏移来决定replication位点信息
  2. GTID模式,通过GTID信息来决定replication位点信息

如果按apply binglog的方式来区分,又可以分为

  1. 串行,按binlog event顺序依次执行

不论哪种replication, 都离不开replication最基本的组件,

  1. IO thread,负责从master拉取binlog.
  2. SQL thread,负责apply relay log binlog.

复制过程中,由于网络或者master主机宕机,都会造成slave IO thread异常中断。 例如以下事务在复制过程中发生上述异常,

那么备库接收的binlog可能不包含完整的事务,备库可能仅接收到BEGIN,也可能只接收到INSERT row1.

然而,当IO thread恢复后,SQL线程怎么正确处理这种异常呢?

  1. GTID模式下,设置auto_position=1时,slave会根据GTID信息,从事务起点开始,重新将事务完整binlog发给备库。此时,备库需要回滚之前的部分事务。

  2. GTID模式下,设置auto_position=0或非GTID模式下,slave会根据位点信息从master续传之前的binlog。此时,备库可以继续完成之前的部分事务。

继续执行事务比较简单,但是回滚之前的部分事务就比较复杂.

分为两种情况来分析:

  • 并行复制有别于串行复制,binlog event由worker线程执行。按串行复制的方式来回滚事务是行不通的,因为重新发送的事务binlog并不一定会分配原来的worker来执行。因此,回滚操作需交给coordinate线程(即sql线程)来完成。 GTID模式下,设置auto_position=1时. IO thread重连时,都会发送 ROTATE_LOG_EVENT和FORMAT_DESCRIPTION_EVENT. 并且FORMAT_DESCRIPTION_EVENT的log_pos>0. 通过非auto_position方式重连的FORMAT_DESCRIPTION_EVENT的log_pos在send之前会被置为0. SQL线程通过执行FORMAT_DESCRIPTION_EVENT且其log_pos>0来判断是否应进入回滚逻辑。而回滚是通过构造Rollback event让work来执行的。
    具体参考

MySQL官方针对此问题有过多次改进,详见以下commit

666aec4a9e976bef4ddd90246c4a31dd456cbca3
3f6ed37fa218ef6a39f28adc896ac0d2f0077ddb
9e2140fc8764feeddd70c58983a8b50f52a12f18

当slave SQL线程处于部分事务异常时,按上节的逻辑,IO thread恢复后,复制是可以正常进行的。但如果IO thread如果长时间不能恢复,那么SQL apply线程会一直等待新的binlog, 并且会一直持有事务中的锁。当slave切换为master后,新master会接受用户连接处理事务,这样SQL apply线程持有的事务锁,可能阻塞用户线程的事务。这是我们不希望看到的。

此时可以通过stop slave来停止SQL apply线程,让事务回滚释放锁。

另一种更好的方案是让SQL apply 线程自动识别这种情况,并加以处理。比如,增加等待超时机制,超时后自动kill sql 线程或回滚SQL线程的部分事务。