作者:陈俊超
本文详细描述了PhxSQL的设计与实现。从MySQL的容灾缺陷开始讲起,接着阐述实现高可用强一致的思路,然后具体分析每个实现环节要注意的要点和解决方案,最后展示了PhxSQL在容灾和性能上的成果。
设计背景
互联网应用中账号和金融类关键系统要求和强调强一致性及高可用性。当面临机器损坏、网络分区、主备手工或者自动切换时,传统的MySQL主备难以保证强一致性和高可用性。PhxSQL将MySQL集群构建在一致性完善的Paxos协议基础上,保证了集群内MySQL机器之间数据的强一致性和整个集群的高可用性。
原生MySQL的容灾缺陷 【MySQL容灾方案】
MySQL有两种常见的复制方案,异步复制和半同步复制。
1、异步复制方案
Master对数据进行commit操作后再将数据异步复制到Slave。
但数据无法保证成功复制,也就无法保证MySQL主备间的数据一致性,如图1所示。
图1 MySQL异步复制流程
2、半同步复制方案
Master对数据进行commit操作前将数据复制到Slave,确认复制成功后再对数据进行commit操作。
绝大多数情况下,半同步复制能保证MySQL主备间的数据一致性,如图2所示。
图2 MySQL半同步复制流程
【MySQL重启流程】
半同步方案中的“半”是指Master在等待Slave的ACK失败时将退化成异步复制。同时,MySQL在重启时也不会执行半同步复制。
如图3中的id(Gtid)=101数据是Master机器中新写入到Binlog File的Binlog数据。但Master在复制数据到Slave的过程中MySQL宕机导致复制失败。MySQL重启时,数据(id=101)会被直接进行commit操作,随后再将数据异步复制到Slave。(下文将已经写入到Binlog File但未进行commit操作的数据(id=101)称为Pending Binlog。)
图3 MySQL重启时直接提交Pending Binlog
该情况下MySQL容易出现Master-Slave之间数据不一致的情况,官方也描述了该问题。
?id=80395
https://mariadb.atlassian.net/browse/MDEV-162
【MySQL重启缺陷】
下面将解释MySQL在重启时不执行半同步会产生数据不一致的原因。
当对上述例子中的Pending Binlog(id=101)进行复制时Master宕机导致复制失败,随后Slave1切换成新Master并开始提供服务(写入id=201的数据)。此后,当旧Master重启时,Pending Binlog(id=101)不会被重新进行复制而直接进行commit操作,从而导致旧Master比新Master多了一条数据,旧Master无法成为新Master的Slave,需要人工处理掉这条数据之后,才能让旧Master作为Slave提供服务,如图4所示。
图4 MySQL重启缺陷导致主备数据不一致
上述case只对旧Master的数据造成影响,不会使得MySQL Client读取到错误数据。但当Master连续出现两次宕机后产生Master切换,两次宕机间隔较短使得Pending Binlog未能及时复制到Slave,且期间有查询请求时(Master宕机→Master重启→查询数据→Master宕机→Master切换),MySQL Client会产生如图5所示的幻读(两次读到的结果不一致)。
图5 MySQL重启缺陷导致Client产生幻读
【MySQL Client分裂】
当Master出现故障且产生Master切换时,由于原生MySQL缺乏调用端的通知/重定向机制,使得不同的Client可能访问不同的Master,导致数据的错误写入和读取,如图6所示。
图6 MySQL进行Master导致Client端分裂
【MySQL缺乏自动选主机制】
由于半同步复制不需要等待所有Slave的ACK,因此当Master出现故障时,需要选有最新Binlog的Slave为新的Master;而MySQL并没有内置这个选主机制,如图7所示。
图7 MySQL缺少自动选主机制
【MySQL的容灾缺陷总结】
MySQL在容灾方面存在的问题:
Master切换时主备数据不能保证一致:Master重启并切换可能导致MySQL主备间数据不一致。Master重启并切换可能导致MySQL Client产生幻读。
原生MySQL缺乏高可用机制:Master切换导致调用端分裂。缺乏自动选主机制。
对于原生MySQL,在高可用和强一致两个特性中,只能二选一:
要求MySQL主备间的数据强一致,不做主备自动切换。
借助MHA实现高可用,容忍MySQL主备间的数据不一致。
因此MySQL在容灾上无法同时满足数据强一致和服务高可用两个特性。
PhxSQL设计思路
【可靠日志存储】
实现一个以可靠日志存储为中心的架构来解决MySQL数据复制时产生的数据不一致问题。
Master将Binlog发送到BinlogSvr集群(可靠日志存储),Slave从BinlogSvr集群获取Binlog数据完成数据复制。
Master在重启时,根据BinlogSvr集群的数据判断Pending Binlog是否已经被复制。如果未被复制则从Binlog File中删除。