MySQL 半同步复制的数据一致性探讨
MySQL 是一个 RDBMS(关系型数据库管理系统),由瑞典 MySQL AB 公司开发,目前属于 Oracle 旗下产品。由于其体积小、速度快、拥有成本低,尤其是开放源码这一特点,广受各大企业欢迎,包括腾讯,阿里,百度,网易,Google,FaceBook 等互联网巨头企业。
随着互联网的高速发展,互联网服务可用性变得越发重要,数据容灾也随之成为各企业的关键任务。在数据容灾中,数据库集群如何处理数据一致性也成为了各企业需要解决的问题。特别在一些新兴的金融服务中,MySQL 也逐渐成为其核心数据库,如何保证金钱的准确性则尤为重要。MySQL 也从一开始的异步复制,到 Google 开发的半同步复制,到 MySQL 5.7 更新的 lossless 半同步复制,一直在优化集群的数据一致性问题。
虽然 MySQL 一直在优化数据的一致性问题,但问题依然存在,使得各大企业纷纷各自设计一套 MySQL 补丁来保证数据一致。腾讯数平的 TDSQL,腾讯微信的 PhxSQL,阿里的 AliSQL,网易的 InnoSQL 等设计都是为了保证数据一致性。MySQL5.7 发布的 lossless 半同步,虽然宣称 zero loss,解决了 5.6 版本中有可能出现的 data lost 问题,但其数据一致性仍未完全解决。
关于 MySQL 的具体介绍可以参考 MySQL 官方 PPT http://www.slideshare.net/andrewjamesmorgan/mysql-high-availability-solutions-feb-2015-webinar
MySQL 半同步复制的问题
图 1 MySql 半同步流程
图 1 描述了 MySQL 的 Binlog 半同步过程。Wait ACK 是半同步的关键步骤,Master 把 Binlog 发给 Slave 之后,需要等待 Slave 的 ACK。Master 直到成功收到 ACK 之后,才执行 Engine Commit 把数据持久化到 Storage。具体细节可参考:http://my-replication-life.blogspot.com/2013/09/loss-less-semi-synchronous-replication.html
MySQL 启动时,Wait ACK 过程会被跳过,导致 Engine Commit 会被直接执行。具体细节请参考:https://jira.mariadb.org/browse/MDEV-162
下面对 MySQL 的数据在 Master 和 Slave 之间是否能保证一致进行简单分析。讨论均基于各机器数据最终是否一致来展开。下面的分析只针对半同步复制,且假设半同步失败后不会退化成异步复制。
场景 1:Master 正常工作
Master 的数据复制到 Slave,Slave 与 Master 保持数据一致。
场景 2:Master Crash 且不切换 Master
场景 2.1
Master 已经收到 ACK,并执行 Engine Commit。Slave 与 Master 保持数据一致。
场景 2.2
Master 处于 Wait ACK 阶段,存在 PendingBinlog(未执行 Engine Commit 的 Binlog)。
图 2 Master 重启时执行 EngineCommit,并把 Binlog 重新复制给 Slave
Master 重启时执行 EngineCommit。Slave 重新连接 Master,Binlog 重新开始复制,随后 Slave 数据和 Master 一致。如图 2。
因此,在 MySql5.7 的情况下,场景 2.2 能保证 Master 和 Slave 之间的数据一致性。但是在 MySQL5.6 及之前的版本,场景 2.2 是不能保证数据一致性的,具体请参考:http://my-replication-life.blogspot.com/2013/09/loss-less-semi-synchronous-replication.html
场景 3:Master Crash 且切换 Master
场景 3.1
旧 Master Crash 时,已经收到至少一台 Slave 的 ACK 并执行 Engine Commit。
数据已复制到至少一台 Slave,该 Slave 与旧 Master 的数据保持一致。
场景 3.2
旧 Master 处于 Wait ACK 阶段时 Crash,新 Master 被切换到了一台拥有最新 Binlog 的 Slave。
场景 3.2 中,旧 Master 中的 PendingBinlog 存在两种场景。
场景 3.2.1
旧 Master Crash 时 Binlog 发送失败,未复制给任何 Slave。
图 3 机器 A 重启 Commit Transaction X。机器 A/B 数据不一致。
图 4 机器 B 接收到事务 X 的重试请求(事务 X’)且复制到机器 A。
机器 A/B 数据可能不一致。
假设机器 A 为旧 Master,执行事务 X 时,复制失败并 Crash。随后机器 B 成为新 Master。机器 A 重启时执行 Engine Commit,事务 X 被 Commit。此时机器 A 和机器 B 的数据一致性被破坏。两台机器上数据可能不一致。如图 3,图 4。
数据不一致的原因是机器 A 在重启时对 PendingBinlog 执行 Engine Commit。在切换了 Master 的情况下,只能通过回滚 PendingBinlog 解决。
MySQL 半同步插件的维护者也提到了类似的想法:http://my-replication-life.blogspot.com/2013/09/loss-less-semi-synchronous-replication.html
场景 3.2.2
旧 Master Crash 时 Binlog 发送成功,但还未执行 Engine Commit。
图 6 机器 A 重启马上执行 Engine Commit,数据一致
假设机器 A 为旧 Master,执行事务 X 时在执行 Commit 前 Crash,但机器 B 收到事务 X。随后机器 B 成为新 Master。
机器 A 重启时对 PendingBinlog 执行 Engine Commit,执行成功后机器 A 的数据是机器 B 的子集。此时机器 A 可从机器 B 中拉取最新的数据。另外一台 Slave 机器 C 可以从这两台机器中任意拉取。
从图 6 可以看出,机器 A 在出现故障时,由于 TransactionX 已经复制给其中一台 Slave 和重启时立刻 Commit Transaction X,使得该 Slave 和 Master 的数据能保证一致。
图 7 两台机器出现故障,Master 切换可能会丢失数据
上述讨论都是基于拥有最新数据的 Slave 和 Master 不能一起出现故障。当这两台机器一起出现故障时,进行 Master 切换则会造成数据丢失。如图 7。
对于较小的集群(机器数目小于或者等于 3),当出现两台机器一起发生故障时,可认为集群已无法提供服务(半同步复制无法工作)。
对于较大的集群(机器数目大于 3),当出现两台机器一起发生故障,且无法得知该两台机器的数据状态时,该集群也无法提供服务(无法确认拥有最新数据的 Slave 是否包含在故障机器中)。因此,对于较大的集群,通常增加半同步复制等待 ACK 的数目,使得出现上述状况时,仍能进行 Master 切换(非故障机器中,存在拥有最新数据的机器)。
增加等待 ACK 的数目,解决了数据丢失的问题,但同时给数据回滚带来了难题。
图 8
如图 8。假设 MySQL 集群有 5 台机器,半同步复制需要等待 2 台 Slave 的 ACK。机器 A 为旧 Master,在执行 Wait ACK 阶段,机器 B 收到 Binlog 后,机器 A 和机器 B 同时 Crash 或者被隔离,导致 Binlog 复制失败。根据场景 3.2.1 的分析,当机器 C 成为 Master 后,机器 A 和机器 B 在恢复服务前需要对其进行数据回滚。但对 Slave 进行数据回滚较为困难。且若回滚失败,则会出现数据不一致。
对于较小的集群,回滚 PendingBinlog 比较容易实现。但对于较大的集群,回滚 PendingBinlog 本身就是一个未解决的难题。
MySQL 的 Master 切换问题
Master 如何切换同时也是 MySQL 容灾中的一个难题。
一个简单的 Master 切换步骤:
Pause 旧 Master
Start 新 Master
更换 MySQLClient 的 Master 指向 IP
存在以下几个问题:
当 Master 被隔离时,如何将其变更为 Slave
解决方法:可修改 MySQL 的代码,使用 zookeeper 等外部辅助服务来自动维护 Master 的状态,可解决 Master 被隔离后不能操作的问题。
如何定位拥有最新 Binlog 数据的 MySQL
解决方法:可以通过人工,或者使用外部工具来检测集群每台 MySQL 的数据。但当出现故障机器无法访问时,无法定位。
如何进行数据回滚
解决方法:可以通过运维进行人工操作。
如何同时更换 MySQLClient 的 Master 指向 IP
同时更换所有 MySQLClient 的 Master 指向 IP 是一件不可能的事情,因为不可能同一时刻操作所有机器。
不能同时更换所有 MySQLClient 的 Master 指向 IP,导致部分 Client 会向旧 Master 发送请求,即出现多个 Master 同时服务。在使用半同步复制的情况下,多台 MySQL 不能同时知道 Master 的去向,使得数据可能产生不一致的情况。
图 9
图 10
假设机器 A 是旧 Master,机器 B 是新 Master,机器 C 还没收到 Master 更换的通知仍然向机器 A 复制 Binlog。User1 在 Master 切换前已经连上机器 A 并持续写入数据。User2 在 Master 切换后开始向机器 B 写入数据。由于机器 A 能把数据复制给机器 C,机器 B 能把数据复制给机器 A,因此机器 A 和机器 B 都能成功写入。如图 9。
由于机器 A 和机器 B 同时写入数据,数据一致性无法保证。如图 10。
总结
从上面分析来看,MySQL 的半同步复制和 Master 切换都存在一些不足。数据复制存在回滚难题,Master 切换存在多 Master 难题。只有解决了这两大难题,才能保证 MySQL 集群的数据一致性。
本文系微信后台团队,如有侵犯,请联系我们立即删除
OpenIMgithub 开源地址:
https://github.com/OpenIMSDK/Open-IM-Server
**OpenIM 官网 :**https://www.rentsoft.cn
**OpenIM 官方论坛:**https://forum.rentsoft.cn/
更多技术文章:
开源 OpenIM:高性能、可伸缩、易扩展的即时通讯架构https://forum.rentsoft.cn/thread/3
【OpenIM 原创】简单轻松入门 一文讲解 WebRTC 实现 1 对 1 音视频通信原理https://forum.rentsoft.cn/thread/4
评论