本文介绍了MySQL半同步复制的历史、原理(包括5.6的半同步和5.7增强后的半同步),相关参数选项,实际环境搭建,半同步复制搭建步骤,模拟故障和恢复等。(以MySQL 8.0版本为例)
目录
一、半同步复制技术
我们知道,普通的replication,即MySQL的异步复制,依靠MySQL二进制日志也即binary log进行数据复制。比如两台机器,一台主机(master),另外一台是从机(slave)。
1)正常的复制为:事务一(t1)写入binlog buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave的io线程接收到t1并写入到自己的的relay log;slave的sql线程写入到本地数据库。 这时,master和slave都能看到这条新的事务,即使master挂了,slave可以提升为新的master。
2)异常的复制为:事务一(t1)写入binlog buffer;dumper线程通知slave有新的事务t1;binlog buffer进行checkpoint;slave因为网络不稳定,一直没有收到t1;master挂掉,slave提升为新的master,t1丢失。
3)很大的问题是:主机和从机事务更新的不同步,就算是没有网络或者其他系统的异常,当业务并发上来时,slave因为要顺序执行master批量事务,导致很大的延迟。这时slave接替Master,就会缺少数据。
为了弥补(应该是部分弥补)以上几种场景的不足,MySQL从5.5开始推出了半同步复制。相比异步复制,半同步复制提高了数据完整性,因为很明确知道,在一个事务提交成功之后,这个事务就至少会存在于两个地方。即在master的dumper线程通知slave后,增加了一个ack(消息确认),即是否成功收到t1的标志码,也就是dumper线程除了发送t1到slave,还承担了接收slave的ack工作。如果出现异常,没有收到ack,那么将自动降级为普通的复制,直到异常修复后又会自动变为半同步复制。
半同步复制具体特性:
- 从库会在连接到主库时告诉主库,它是不是配置了半同步。
- 如果半同步复制在主库端是开启了的,并且至少有一个半同步复制的从库节点,那么此时主库的事务线程在提交时会被阻塞并等待,结果有两种可能,要么至少一个从库节点通知它已经收到了所有这个事务的Binlog事件,要么一直等待直到超过配置的某一个时间点为止,而此时,半同步复制将自动关闭,转换为异步复制。(异步复制兜底)
- 从库节点只有在接收到某一个事务的所有Binlog,将其写入并Flush到Relay Log文件之后(写到Relay log文件既可,不等待Slave加载应用到数据库中),然后通知对应主库上面的等待线程。
- 如果在等待过程中,等待时间已经超过了配置的超时时间,没有任何一个从节点通知当前事务,那么此时主库会自动转换为异步复制,当至少一个半同步从节点赶上来时,主库便会自动转换为半同步方式的复制。
- 半同步复制必须是在主库和从库两端都开启时才行,如果在主库上没打开,或者在主库上开启了而在从库上没有开启,主库都会使用异步方式复制。
半同步复制仍前置问题:
如上图,半同步复制原理图(MySQL 5.7之前的),Master将每个事务写入binlog(sync_binlog=1),传递到slave刷新到磁盘(sync_relay=1),同时主库提交事务(commit)。master等待slave反馈收到relay log,只有收到ACK后master才将commit OK结果反馈给客户端。
在MySQL 5.5~5.6使用AFTER_COMMIT的选项下,既Master先commit再等待Slave ACK:客户端事务在存储引擎层COMMIT后,在等待从库确认的过程中,这时主库宕机了。此时,即主库在等待Slave ACK的时候,虽然Master没有返回当前客户端已提交完毕,但实际上Master库的事务已经提交并写入磁盘,其他客户端若读取则会读取到已提交事务。
如果Slave端还没有读到该事务的events(既Slave还没有跟上主库),就在这时,主库发生了crash,然后切换到Slave(Slave升级为主库)。那么之前其他客户端能读到的事务就不见了,出现了幻读。如下图所示,图片引自Loss-less Semi-Synchronous Replication on MySQL 5.7.2。
如果主库永远启动不了,那么实际上在主库已经成功提交的事务,在从库上是永远找不到的,也就是数据丢失了,这是MySQL不愿意看到的。所以在MySQL 5.7版本中增加了AFTER_SYNC选项,并将其设置为默认半同步方式,一定程度上避免了此问题。
因此,建议半同步一律设置为AFTER_SYNC,不要再设置为AFTER_COMMIT了。