简介: Raft提出的两阶段成员变更Joint Consensus是业界主流的成员变更方法,极大的推动了成员变更的工程应用。但Joint Consensus成员变更采用两阶段,一次变更需要提议两条日志, 在一些系统中直接使用时有些不便。那么Joint Consensus成员变更能否只使用单步实现呢?

作者 | 祥光
来源 | 阿里技术公众号

一 引言

分布式系统运行过程中节点经常会出现故障,需要支持节点的动态增加、删除和替换。
成员变更是分布式系统绕不开的话题,特别是在一致性系统中,对于提升运维能力和服务可用性都有很大的帮助。

Raft提出的两阶段成员变更Joint Consensus是业界主流的成员变更方法,极大的推动了成员变更的工程应用。但Joint Consensus成员变更采用两阶段,一次变更需要提议两条日志, 在一些系统中直接使用时有些不便。虽然Raft也提出了单步成员变更方法,但单步成员变更方法一次只能增加或减少一个成员,限制较大,并且容易踩坑,一般不推荐使用。

那么很自然的想到,Joint Consensus成员变更能否只使用单步实现呢?本文对这个问题进行了深入探讨。

二 成员变更

我们先来回顾下一致性协议中的成员变更问题。成员变更是在集群运行过程中改变运行一致性协议的节点,如增加、减少节点、节点替换等。成员变更过程不能影响系统的可用性。

成员变更也是一个一致性问题,即所有节点对成员配置达成一致。但是成员变更又有其特殊性,因为在成员变更的过程中,参与投票的成员会发生变化。

图1 成员变更的某一时刻Cold和Cnew中同时存在两个不相交的多数派

如果将成员变更当成一般的一致性问题,成员变更过程中,各节点从旧成员配置Cold切换到新成员配置Cnew的时刻可能有差异,可能在某一时刻Cold和Cnew中同时存在两个不相交的多数派,形成双Quorum,破坏一致性。

为了解决这个问题,Raft提出了两阶段的成员变更方法Joint Consensus。

1 Joint Consensus成员变更

Joint Consensus成员变更为了避免双Quorum问题,引入一个联合成员配置Cold,new作为过渡配置, Cold,new是Cold和Cnew的组合。Cold与Cold,new的Quorum有交集,Cold,new与Cnew的Quorum也有交集。成员变更先从Cold切换到Cold,new,待Cold,new提交后,再切换到Cnew,保证Cold与Cnew不同时使用,因而不会形成双Quorum,保障安全性。

图2 Cold与Cold, new与Cnew三者的Quorum集合之间的关系

Joint Consensus使用两条日志完成成员变更过程。Leader收到成员变更请求后,先向Cold和Cnew同步一条Cold,new日志,此后所有日志都需要Cold和Cnew两个多数派的确认。Cold,new日志在Cold和Cnew都达成多数派之后才能提交,此后Leader再向Cold和Cnew同步一条只包含Cnew的日志,此后日志只需要Cnew的多数派确认。Cnew日志只需要在Cnew达成多数派即可提交,此时成员变更完成,不在Cnew中的成员自动下线。

图3 Joint Consensus成员变更过程

成员变更过程中如果发生Failover,老Leader宕机,Cold,new中任意节点都可能成为新Leader,如果新Leader上没有Cold,new日志,则继续使用Cold,Follower上如果有Cold,new日志会被新Leader截断,回退到Cold,成员变更失败;如果新Leader上有Cold,new日志,则继续将未完成的成员变更流程走完。

2 单步成员变更

Joint Consensus成员变更之所以需要两个阶段,是因为对Cold与Cnew的关系没有做任何假设,为了避免Cold和Cnew各自形成不相交的多数派而形成双Quorum,才引入了两阶段方案。

如果增强成员变更的限制,假设Cold与Cnew的Quorum交集不为空,Cold与Cnew就无法形成双Quorum,则成员变更就可以简化为一阶段。

实现单步的成员变更,关键在于限制Cold与Cnew,使Cold与Cnew的Quorum交集不为空。那么怎么样限制Cold与Cnew,才能使Cold与Cnew的Quorum交集不为空呢?方法就是每次成员变更只允许增加或删除一个成员。

图4 增加或删除一个成员时Cold与Cnew的Quorum

增加或删除一个成员时的情形,如图4所示,可以从数学上严格证明,只要每次只允许增加或删除一个成员,Cold与Cnew不可能形成两个不相交的Quorum。因此只要每次只增加或删除一个成员,从Cold可直接切换到Cnew,无需过渡成员配置,实现单步成员变更。

单步成员变更一次只能变更一个成员,如果需要变更多个成员,如实现替换成员等,可以通过执行多次单步成员变更来实现。

单步成员变更理论虽然简单,但却埋了很多坑,实际用起来并不是那么简单。先前的文章Raft成员变更的工程实践中有详细介绍。

三 两阶段成员变更的单步实现

Joint Consensus成员变更虽然通用但是采用两阶段,一次变更需要提交两条日志,单步成员变更虽然只需要提交一条日志,但是限制较大,一次只能变更一个成员。两者的优势能否结合呢?Joint Consensus成员变更能否只用单步实现呢?

Joint Consensus成员变更过程中,Cold,new日志的提交已经让各节点对Cnew配置达成了一致,那么Cnew日志有什么作用呢?能否在Cold,new日志提交后就从Cold,new配置切换到Cnew配置呢?这样是不是就可以不需要Cnew日志,变成单步实现了呢?

考虑Joint Consensus成员变更中Cnew日志的作用,Cnew日志在Cold,new日志提交之后发起提议,节点收到并持久化Cnew日志后从Cold,new配置切换到Cnew配置,不在Cnew配置中的成员在Cnew日志提交后下线。根据这个过程,可以总结出Cnew日志的作用:

  1. 通知节点在收到并持久化Cnew日志后从Cold,new配置切换到Cnew配置。
  2. 通知不在Cnew配置中的节点在Cnew日志提交后下线。
  3. 成员变更过程中发生Failover后,本地有Cnew日志的节点具有优先选举权。

如果能不使用Cnew日志同时又完成Cnew日志的工作,不就可以用单步实现两阶段的Joint Consensus成员变更吗?事实上已经有系统探索过这条路。

1 ZooKeeper成员变更

ZooKeeper从3.5.0版本开始在Zab的基础上支持了成员变更。ZooKeeper具有Primary Order特性,而使用两条日志的Joint Consensus成员变更无法保证Primary Order特性,为了既满足成员变更的通用性,又不丧失Primary Order特性,ZooKeeper在论文《Dynamic Reconfiguration of Primary/Backup Clusters》中提出了自己的成员变更方法,并在ZooKeeper中应用了此方法,比Raft的提出还早。

如图5是ZooKeeper成员变更协议,图中旧成员配置用S表示,新成员配置用S‘表示,P为Leader节点,图5展示了将B1和B2节点替换成B3和B4节点的过程:

图5 ZooKeeper成员变更协议

初始化:为了让新节点追上最新数据,新成员配置S’中的新节点B3、B4先连接到当前的主节点P,P会向它们传输自己当前的状态作为他们的初始状态。在Zab协议中当备节点连接上主节点时这样的状态传输就会自动发生,并且会继续从主节点P接收所有后续的操作日志(例如图中的Op1和Op2),这个过程中节点B3、B4不参与投票。

步骤1:主节点P向连接到它的所有备节点(S U S‘)发送成员变更日志COP,COP日志中携带旧成员配置S和新成员配置S‘,并等待旧成员配置S中的节点确认。一旦S中的多数派确认了COP日志,就对S’达成了共识。

步骤2:在COP日志之前的日志只需要旧成员配置S中的多数派确认,可以在旧成员配置和新成员配置(S U S‘)中提交;在COP命令之后且在S’的激活消息ACTIVATE之前的日志需要新旧成员配置(S U S‘)两个多数派确认,并且只能在S’中提交;在S’的激活消息ACTIVATE后的日志,只需要在S‘中确认和提交。

步骤3:主节点P等待COP日志以及S'中COP之前的日志的确认。

步骤4:一旦新旧成员配置(S U S1)两个多数派都确认了COP日志,主节点P就提交COP日志,并广播一条激活消息ACTIVATE来激活新成员配置S’从而完成成员变更。与日志同步消息类似,ACTIVATE消息包含主节点P的Epoch,携带过时的Epoch的ACTIVATE消息将被忽略。

成员变更过程中如果发生Failover,可能出现下面几种情况:

如果在COP日志发送之前Failover,那么成员变更失败,在旧成员配置中重新选主后继续工作;

如果在COP日志发送之后并且在ACTIVATE之前Failover,新旧成员配置中任意节点都可能成为新Leader,如果新Leader上没有COP日志,则成员变更失败;如果新Leader上有COP日志,则继续将未完成的成员变更流程走完。

如果在ACTIVATE后Failover,成员变更已经完成,但还无法保证新Leader一定在新成员配置中,此时不在新成员配置中的节点还不能下线。因此在发送ACTIVATE消息后还需要在新成员配置中提交一条no-op日志,no-op日志提交后可保证新Leader一定在新成员配置中,不在新成员配置中的节点可以安全下线。

ZooKeeper利用异步的Commit消息,也即ACTIVATE消息来通知节点从新旧成员配置切换到新成员配置。使用异步的no-op日志让不在新成员配置中的节点安全下线。ZooKeeper的ACTIVATE消息和异步的no-op日志起到了Joint Consensus成员变更中Cnew日志的作用。

2 改进的单步实现

ZooKeeper成员变更协议不如Joint Consensus成员变更那么简洁,Joint Consensus成员变更通过两阶段可以利用协议本身而不需要做过多的限制来保证成员变更的安全性。那么ZooKeeper成员变更协议是否可以改进呢?

ZooKeeper成员变更协议中异步的ACTIVATE消息和no-op日志其实就是为了完成Joint Consensus成员变更中Cnew日志的作用,明白了这一点后那么也可以将Joint Consensus成员变更的Cnew日志改为异步的,在Cold,new日志提交后就认为成员变更完成,然后异步的提交Cnew日志。之所以可以将Cnew日志改为异步的,在Cold,new日志提交后就认为成员变更完成,是因为Cold,new日志一旦提交,各节点已经对新成员配置达成了一致,再也不会回退到旧成员配置了,剩下的过程最终一定会执行完成,Cnew日志最终一定会提交。

还有一种改进方法是继续保留ACTIVATE消息,但不使用no-op日志,那么怎么样保证切换到新成员配置的节点具有优先选举权呢?根据选举的安全性,具有最新日志的节点具有优先选举权,那么可以在选举的时候携带节点当前的成员配置,在日志一样新的情况下,优先给已经切换到新成员配置的节点投票,即可保证切换到新成员配置的节点具有优先选举权。新成员配置中的大多数节点切换到新成员配置后,不在新成员配置中的节点可以安全下线。

四 总结

Joint Consensus成员变更的提出极大的推动了成员变更的工程应用,其简洁优美并且通用,但是采用两阶段,一次变更需要提交两条日志。本文探讨了两阶段的Joint Consensus成员变更的单步实现方法,并做了一些改进,为成员变更的工程应用提供了更多的选择。

原文链接
本文为阿里云原创内容,未经允许不得转载。

Joint Consensus两阶段成员变更的单步实现相关推荐

  1. Raft成员变更的工程实践

    简介: 成员变更是一致性系统实现绕不开的难题,对于提升运维能力以及服务可用性都有很大的帮助. 本文从Raft成员变更理论出发,介绍了Raft成员变更和单步成员变更的问题,其中包括Raft著名的Bug. ...

  2. Raft 集群成员变更、日志压缩、客户端交互

    Raft 集群成员变更.日志压缩.客户端交互 集群成员变更 在集群服务器发生变化时,不能一次性的把所有的服务器配置信息从老的替换为新的,因为,每台服务器的替换进度是不一样的,可能会导致出现双主的情况, ...

  3. 【Raft】学习九:成员变更ConfChangeV2

    前言 在分布式系统中,节点的增删是常见也是必须的操作,对于实用的共识算法Raft自然也提供了关于节点变更的理论基础.在Raft算法中一次变更一个节点是天然支持的,如果一次涉及多个节点的变更,对于一个稳 ...

  4. raft中集群成员变更

    文章目录 1. raft集群变更再学习 2. 单阶段过度的风险分析 3. 两阶段过度的完整性分析 4. 集群变更时可能存在的其他问题 1. 新加入的节点没有日志 2. 集群领导人可能需要下线 3. 从 ...

  5. CAP原理,分布式一致性算法,两阶段提交,三阶段提交,Paxos,Raft,zookeeper的选主过程,zab协议,顺序一致性,数据写入流程,节点状态,节点的角色

    我们知道,一般在分布式应用都有CAP准则: C Consistency, 一致性,分布式中的各个节点数据保持一致 A availability 可用性,任何时刻,分布式集群总是能够提供服务 P par ...

  6. ECCV2020 | Gen-LaneNet:百度Apollo提出两阶段的3D车道线检测算法,已开源

    点击上方"3D视觉工坊",选择"星标 干货第一时间送达 这篇文章收录于ECCV2020,是百度Apollo团队发表的关于3D车道线检测的文章,针对3D-LaneNet做了 ...

  7. 分布式事务:两阶段提交与三阶段提交

    两阶段提交与三阶段提交 分布式事务 二阶段提交 请求阶段 提交阶段 举例 故障分析 柜员侧出现故障或拒绝Proposal 第一阶段经理侧出现故障 第二阶段经理侧出现故障 二阶段提交存在的问题 三阶段提 ...

  8. 数据包络分析-两阶段网络弱环节(the weak-link approach)

    文献解读-两阶段网络弱环节 Three approaches independent assessments joint assessment:A holistic approach The comp ...

  9. 星巴克不使用两阶段提交

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:r6d.cn/6dyh 译者序 本文翻译自 2004 ...

最新文章

  1. 在windows下安装PyPdf2
  2. 20应用统计考研复试要点(part1)--统计学
  3. php 查询and or,php – SQL查询多个AND和OR不起作用
  4. sde oracle11g,Arcsde post oracle11g报错解决办法
  5. 2020“家”经济时代开启——中国到家服务行业研究报告
  6. Spring框架 AOP面向切面编程(转)
  7. byte初始化并赋值_一位数组的定义、赋值和初始化.note
  8. The developer claims that Bpytop
  9. 用c语言实现相机坐标的过滤,华为2014年机试题【字符串过滤】-【C语言/C++】
  10. Leetcode639. Decode Ways II
  11. 螺旋测微器b类不确定度_数控铣床G02、G03圆弧指令,还能作为螺旋插补用于油槽加工!...
  12. cc2530设计性实验代码五
  13. Photoshop插件-增加细节-脚本开发-PS插件
  14. java计算机毕业设计中小型超市管理系统录像补源码+数据库+系统+lw文档+mybatis+运行部署
  15. python 报警声音的实现
  16. 模N计数器-计数+使能信号
  17. 聚醚/聚丙烯酰胺-竣甲基/聚丙烯酰胺/粒状聚N-异丙基丙烯酰胺壳聚糖水凝胶的制备方法
  18. python拼图_利用python制作拼图小游戏的全过程
  19. 小知识·typec耳机原理
  20. 数据分析的心法、手法和利器

热门文章

  1. 软件加入使用时间_你有了抢口罩软件,电商也进行了升级,我们又有了新软件(3月2日更新)...
  2. mysql修改密码1820_mysql5.7初始化密码报错ERROR1820(HY000):YoumustresetyourpasswordusingALTERUSERstateme...
  3. 全国战争linux添加eth0,linux服务器双线路接入配置
  4. layui option 动态添加_layui select动态添加option的实例
  5. 华硕 x86 android,【华硕X79评测】学不会不收费 几步教你安装Android x86-中关村在线...
  6. 【LeetCode笔记】85. 最大矩形(Java、单调栈)
  7. m 文件 dll matlab 中调用_如何在matlab中调用python程序
  8. python怎么定义正方形函数_python – Matplotlib自定义图例以显示正方形而不是矩形...
  9. python中字典的键是唯一的吗_在python3中反转字典中的键和值(值不唯一)
  10. python原理书籍_python书籍推荐:《深入浅出深度学习:原理剖析与Python实践》