在 Meta 构建和部署 MySQL Raft( 三 )


文章插图
 
崩溃恢复必须对崩溃恢复进行更改,以使其与 Raft 无缝协作 。在交易的生命周期中,崩溃随时可能发生,因此协议必须确保成员的一致性 。以下是有关我们如何使其发挥作用的一些重要见解 。

  1. Transaction was not flushed to binlog:在这种情况下,内存中的事务负载(仍然在 mysqld 进程内存中作为内存缓冲区)将丢失,并且引擎中准备好的事务将在进程重新启动时回滚 。由于 Raft 日志中没有多余的未提交事务,因此不需要与其他成员进行对账 。
  2. 事务被刷新到 binlog 但从未到达其他成员:Mysqld 充当事务协调器并作为参与者在引擎和复制的 binlog 之间运行两阶段提交协议 。在崩溃恢复时,引擎(例如 InnoDB 或 MyRocks)中准备好的事务将被回滚(引擎尚未提交) 。Raft 将进行故障转移,并选举出新的领导者 。该领导者不会在其 binlog 中包含此事务,并且此后将从前领导者的 binlog 中截断此事务,因为当前任领导者重新加入环时(通过推送 No-Op 消息) 。
  3. 事务被刷新到 binlog 并到达下一个领导者 。Current leader 在提交给引擎之前就死了:类似于 no 。2 上面,引擎中准备好的事务将被回滚 。以前的领导者将作为追随者加入 Raft 环 。在这种情况下,新领导者将在其二进制日志中包含此事务,因此不会发生截断,因为日志会匹配 。当新领导者发送提交标记时,事务将从头开始重新应用 。
Raft 启动的状态机转换故障转移和定期维护操作可以触发 Raft 中的领导层变更 。选出领导者后,MyRaft 插件将尝试将伴随的 MySQL 转换为主要模式 。为此,该插件将编排一组步骤 。这些来自 Raft → MySQL 的回调将中止正在进行的事务,回滚正在使用的 GTID,将引擎端日志从应用日志转换为二进制日志,并最终设置正确的只读设置 。这个机制比较复杂,目前还没有开源 。
灵活筏由于Raft 论文和 Apache Kudu 仅支持单个全局仲裁,因此它在 Meta 上效果不佳,因为环很大但数据路径仲裁需要很小 。
为了规避这个问题,我们在 FlexiRaft 上进行了创新,借鉴了Flexible Paxos 的思想 。
在高层次上,FlexiRaft 允许 Raft 有不同的数据提交法定人数(小),但在领导者选举法定人数(大)上采取相应的命中 。通过遵循群体交集的可证明保证,FlexiRaft 确保 Raft 的最长日志规则和适当的群体交集将保证可证明的安全性 。
FlexiRaft 支持单区域动态模式 。在这种模式下,成员按其地理区域分组在一起 。Raft 的当前法定人数取决于当前领导者是谁(因此称为“单区域动态”) 。数据法定人数是领导者所在地区的大多数选民 。在晋升期间,如果任期是连续的,则候选人将与最后一个已知领导者的区域相交 。FlexiRaft 还会确保也达到 Candidate 区域的法定人数,否则后续的 No-Op 消息可能会卡住 。如果在极少数情况下项不连续,Flexi Raft 将尝试找出一组不断增长的区域,为了安全需要与之相交,或者在最坏的情况下,将退回到 Flexible Paxos 的 N 区域相交情况. 由于预选和模拟选举,
控制平面操作(促销和会员变更)为了序列化binlog中的promotion和membership change事件,我们劫持了MySQL二进制日志格式的Rotate Event和Metadata事件 。这些事件将携带相当于 Raft 的 No-Op 消息和添加成员/删除成员操作 。Apache Kudu 不支持联合共识,因此我们只允许一次更改一个成员资格(您可以在一轮中仅更改一个实体的成员资格以遵循隐式仲裁交集的规则) 。
自动化随着 MySQL Raft 的实施,我们为 MySQL 部署实现了一个非常干净的关注点分离 。MySQL 服务器将通过 Raft 的复制状态机负责安全 。无数据丢失保证将被证明包含在服务器本身中 。自动化(Python 脚本、守护进程)将启动控制平面操作并监控机队的健康状况 。它还会在维护期间或检测到主机故障时通过 Raft 替换成员或进行促销 。偶尔,自动化也可以改变 MySQL 拓扑的区域布局 。改变自动化以适应 Raft 是一项艰巨的任务,跨越了多年的开发和推出工作 。
在长时间的维护事件中,自动化会在 Raft 上设置领导禁止信息 。Raft 将不允许那些被禁止的实体成为领导者,或者在无意的选举中迅速撤离他们 。自动化还将促进从这些地区进入其他地区 。
从部署过程中遇到的挑战中学习将 Raft 部署到舰队对团队来说是一次巨大的学习 。我们最初在MySQL 5.6上开发 Raft ,不得不迁移到MySQL 8.0 。
其中一项重要的经验是,虽然使用 Raft 更容易推理出正确性,但 Raft 协议本身对可用性的关注并没有多大帮助 。由于我们的 MySQL 数据仲裁非常小(三分之二的区域内成员),该地区的两个坏实体几乎可以破坏仲裁并降低可用性 。MySQL 集群每天都会经历大量变动(由于维护、主机故障、重新平衡操作),因此及时正确地启动和执行成员更改是持续可用性的关键要求 。推出工作的很大一部分集中在及时更换 logtailer 和 MySQL,以便 Raft quorums 健康 。


推荐阅读