- 普通的master-slave 异步replication
- 支持多通道的group replication和double binlog
如果按连接协议来区分,又可以分为
- 非GTID模式,通过binlog文件名和文件的偏移来决定replication位点信息
- GTID模式,通过GTID信息来决定replication位点信息
如果按apply binglog的方式来区分,又可以分为
- 串行,按binlog event顺序依次执行
不论哪种replication, 都离不开replication最基本的组件,
- IO thread,负责从master拉取binlog.
- SQL thread,负责apply relay log binlog.
复制过程中,由于网络或者master主机宕机,都会造成slave IO thread异常中断。 例如以下事务在复制过程中发生上述异常,
那么备库接收的binlog可能不包含完整的事务,备库可能仅接收到BEGIN,也可能只接收到INSERT row1.
然而,当IO thread恢复后,SQL线程怎么正确处理这种异常呢?
GTID模式下,设置auto_position=1时,slave会根据GTID信息,从事务起点开始,重新将事务完整binlog发给备库。此时,备库需要回滚之前的部分事务。
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线程的部分事务。