网站上怎样做轮播图,wordpress 换中文,企业网站推广短平快,做ppt的软件怎么下载网站简介#xff1a; 成员变更是一致性系统实现绕不开的难题#xff0c;对于提升运维能力以及服务可用性都有很大的帮助。 本文从Raft成员变更理论出发#xff0c;介绍了Raft成员变更和单步成员变更的问题#xff0c;其中包括Raft著名的Bug。 对于Raft成员变更的工程实现上需要…简介 成员变更是一致性系统实现绕不开的难题对于提升运维能力以及服务可用性都有很大的帮助。 本文从Raft成员变更理论出发介绍了Raft成员变更和单步成员变更的问题其中包括Raft著名的Bug。 对于Raft成员变更的工程实现上需要考虑的问题本文给出了一些工程实践经验。 一 引言 成员变更是一致性系统实现绕不开的难题对于提升运维能力以及服务可用性都有很大的帮助。 本文从Raft成员变更理论出发介绍了Raft成员变更和单步成员变更的问题其中包括Raft著名的Bug。 对于Raft成员变更的工程实现上需要考虑的问题本文给出了一些工程实践经验。 二 Raft成员变更简介 分布式系统运行过程中节点经常会出现故障需要支持节点的动态增加和删除。 成员变更是在集群运行过程中改变运行一致性协议的节点如增加、减少节点、节点替换等。成员变更过程不能影响系统的可用性。 成员变更也是一个一致性问题即所有节点对新成员达成一致。但是成员变更又有其特殊性因为在成员变更的过程中参与投票的成员会发生变化。 如果将成员变更当成一般的一致性问题直接向Leader节点发送成员变更请求Leader同步成员变更日志达成多数派之后提交各节点提交成员变更日志后从旧成员配置Cold切换到新成员配置Cnew。 因为各个节点提交成员变更日志的时刻可能不同造成各个节点从旧成员配置Cold切换到新成员配置Cnew的时刻不同。可能在某一时刻出现Cold和Cnew中同时存在两个不相交的多数派进而可能选出两个Leader形成不同的决议破坏安全性。 图1 成员变更的某一时刻Cold和Cnew中同时存在两个不相交的多数派 如图1是3个节点的集群扩展到5个节点的集群直接扩展可能会造成Server1和Server2构成老成员配置的多数派Server3、Server4和Server5构成新成员配置的多数派两者不相交从而可能导致决议冲突。 由于成员变更的这一特殊性成员变更不能当成一般的一致性问题去解决。为了解决这个问题Raft提出了两阶段的成员变更方法Joint Consensus。 1 Joint Consensus成员变更 Joint Consensus成员变更让集群先从旧成员配置Cold切换到一个过渡成员配置称为联合一致成员配置Joint Consensus联合一致成员配置是旧成员配置Cold和新成员配置Cnew 的组合Cold,new一旦联合一致成员配置Cold,new提交再切换到新成员配置Cnew 。 图2 Joint Consensus成员变更 Leader收到成员变更请求后先向Cold和Cnew同步一条Cold,new日志此后所有日志都需要Cold和Cnew两个多数派的确认。Cold,new日志在Cold和Cnew都达成多数派之后才能提交此后Leader再向Cold和Cnew同步一条只包含Cnew的日志此后日志只需要Cnew的多数派确认。Cnew日志只需要在Cnew达成多数派即可提交此时成员变更完成不在Cnew中的成员自动下线。 成员变更过程中如果发生Failover老Leader宕机Cold,new中任意一个节点都可能成为新Leader如果新Leader上没有Cold,new日志则继续使用ColdFollower上如果有Cold,new日志会被新Leader截断回退到Cold成员变更失败如果新Leader上有Cold,new日志则继续将未完成的成员变更流程走完。 Joint Consensus成员变更比较通用且容易理解但是实现比较复杂之所以分为两个阶段是因为对 与 的关系没有做任何假设为了避免 和 各自形成不相交的多数派而选出两个Leader才引入了两阶段方案。 如果增强成员变更的限制假设Cold与Cnew任意的多数派交集不为空Cold与Cnew就无法各自形成多数派则成员变更就可以简化为一阶段。 2 单步成员变更 实现单步的成员变更关键在于限制Cold与Cnew使之任意的多数派交集不为空。方法就是每次成员变更只允许增加或删除一个成员。 图3 增加或删除一个成员 增加或删除一个成员时的情形如图3所示可以从数学上严格证明只要每次只允许增加或删除一个成员Cold与Cnew不可能形成两个不相交的多数派。因此只要每次只增加或删除一个成员从Cold可直接切换到Cnew无需过渡成员配置实现单步成员变更。 单步成员变更一次只能变更一个成员如果需要变更多个成员可以通过执行多次单步成员变更来实现。 单步成员变更理论虽然简单但却埋了很多坑实际用起来并不是那么简单。 三 Raft单步成员变更的问题 Raft单步成员变更的问题最著名的莫过于Raft著名的正确性问题另外单步成员变更还有潜在的可用性问题。 1 Raft单步成员变更的正确性问题 Raft单步变更过程中如果发生Leader切换会出现正确性问题可能导致已经提交的日志又被覆盖。Raft作者Diego Ongaro早在2015年就发现了这个问题并且在Raft-dev详细的说明了这个问题[1]。 下面是一个Raft单步变更出问题的例子, 初始成员配置是abcd这4节点节点u和V要加入集群, 如果中间出现Leader切换, 就会丢失已提交的日志 图4 Raft单步成员变更的正确性问题 t0节点abcd的成员配置为C0t1 节点abcd在Term 0选出a为Leaderb和c为Followert2节点a同步成员变更日志Cu只同步到a和u未成功提交t3节点a宕机t4节点d在Term 1被选为Leaderb和c为Followert5节点d同步成员变更日志Cv同步到c、d、V成功提交t6节点d同步普通日志E同步到c、d、V成功提交t7节点d宕机t8节点a在Term 2重新选为Leaderu和b为Follower;t9节点a同步本地的日志Cu给所有人造成已提交的Cv和E丢失。为什么会出现这样的问题呢根本原因是上一任Leader的成员变更日志还没有同步到多数派就宕机了新Leader一上任就进行成员变更使用新的成员配置提交日志之前上一任Leader重新上任之后可能形成另外一个多数派集合产生脑裂将已提交的日志覆盖造成数据丢失。 Raft作者在发现这个问题之后也给出了修复方法。修复方法很简单, 跟Raft的日志Commit条件类似新任Leader必须在当前Term提交一条日志之后才允许同步成员变更日志。也即Leader在当前Term还未提交日志之前不允许同步成员变更日志。 按照这个修复方法最简单的实现就是Leader上任后先提交一条no-op日志然后再同步成员变更日志。这条no-op日志可以保证跟上一任Leader未提交的成员变更日志至少有一个节点交集这样可以发现上一任Leader的日志是旧的从而阻止上一任Leader重新选为Leader进而阻止了脑裂的产生。 对应上面这个例子就是L1当选Leader后必须先提交一条no-op日志然后才能开始同步Cv和E以便能发现L2的日志是旧的从而阻止L2当选Leader。 另一种方法是使用Joint Consensus成员变更没有这样的正确性问题。 2 Raft单步成员变更的可用性问题 单步成员变更每次只能增加或者减少一个成员在做成员替换的时候需要分两次变更第一次变更先将新成员加入进来第二次变更再将老成员删除中间如果如果网络分区有可能会导致服务不可用。 考虑a、b、c三个成员部署在三个机房现在因为a发生故障要将a替换为同机房的d。按照单步成员变更abc要先变为abcd再变为bcd。 中间经历的4节点abcd的状态, 有可能在出现二分的网络分区(ad|bc)时导致整个集群不可用。因为a与d位于同一机房这种二分网络分区的情况在实际情况中还是不容忽视的。 怎么解决这个问题呢一种方法是做成员替换的时候先删除老成员再加入新成员即abc先变为bc再变为bcd这样可以避免abcd的状态。 另一种方法是使用Joint Consensus成员变更abc先变为abc U bcd 再变为bcd也不会经历abcd的状态。 四 Raft成员变更的工程实践 Raft成员变更的理论虽简单但实际工程实现上还是有很多地方要考虑。因为Raft单步成员变更有正确性问题及可用性问题工程上建议尽量使用Joint Consensus成员变更这里主要讨论一些Joint Consensus成员变更工程实现上必须考虑的问题。 1 新成员先加入再同步数据还是先同步数据再加入 因为Raft需要严格保证顺序而新成员上还没有任何数据因此新成员加入集群后需要先同步数据才能正常工作。工程实现时就有两种选择一种是让新成员先加入再同步数据另一种是先给新成员同步数据同步完成后再加入。这两种方式各有利弊。 表1 新成员先加入再同步数据和先同步数据再加入的优缺点 新成员先加入再同步数据成员变更可以立即完成并且因为只要大多数成员同意即可加入甚至可以加入还不存在的成员加入后再慢慢同步数据。但在数据同步完成之前新成员无法服务但新成员的加入可能让多数派集合增大而新成员暂时又无法服务此时如果有成员发生Failover很可能导致无法满足多数成员存活的条件让服务不可用。因此新成员先加入再同步数据简化了成员变更但可能降低服务的可用性。 新成员先同步数据再加入成员变更需要后台异步进行先将新成员作为Learner角色加入只能同步数据不具有投票权不会增加多数派集合等数据同步完成后再让新成员正式加入正式加入后可立即开始工作不影响服务可用性。因此新成员先同步数据再加入不影响服务的可用性但成员变更流程复杂并且因为要先给新成员同步数据不能加入还不存在的成员。 2 成员变更日志使用什么配置 成员变更日志本身是为了改变成员配置处在成员配置变更的临界点上因此成员变更日志使用什么配置就很关键。 表2 Joint Consensus成员变更日志使用的成员配置 对于Joint Consensus成员变更成员变更日志使用什么配置是确定的。Cold,new日志使用联合一致成员配置Cold,new需要老成员配置Cold和新成员配置Cnew两个多数派确认才能提交Cnew日志使用新成员配置Cnew只需要新成员配置Cnew的多数派确认即可提交但Cnew日志也会同步给老成员配置Cold主要是为了让Cold中不在Cnew中的成员自动退出。 3 成员变更日志什么时候生效 成员变更通过成员变更日志来完成让各成员对成员配置达成一致但成员变更日志与普通日志不同并不一定要等到提交后Apply生效。 表3 成员变更日志的生效时机 对于Joint Consensus成员变更成员变更日志什么时候生效是确定的。在Leader上开始同步成员变更日志之前就需要生效在Follower上成员变更日志持久化完成后就需要生效。成员变更日志还未提交就先生效了因此在Leader切换后可能会回滚。 4 成员变更期间日志是否需要严格按序提交 考虑这样一种情况成员变更减少了成员数量进而减小了多数派集合而更小的多数派更容易达成造成成员变更之后的日志比之前的日志先达成多数派。 按照Raft论文中的commitIndex的推进算法 If there exists an N such that N commitIndex, a majority of matchIndex[i] ≥ N, and log[N].term currentTerm: set commitIndex N 一条日志达成多数派就往前推进commitIndex至该日志如果该日志之前有日志按照老成员配置还未达成多数派也一并提交了。 这种情况是否会出问题呢实际上并不会因为成员变更之后已经有日志使用新成员配置提交了不在新成员配置中的节点不可能再当选Leader了进而不会覆盖之前的日志因此就算之前的日志按照老成员配置未达成多数派也可以安全的提交。 hashicorp raft的实现还是严格按序提交的即只有前面的日志都达成多数派之后才能提交。 5 只有少数成员存活时怎么恢复服务 Raft只能在大多数成员存活的情况下才能正常工作实际可能会遇到只有少数成员存活的情况这个时候要怎么恢复服务呢。 因为只有少数成员存活已经不能达成多数派不能写入数据也不能做正常的成员变更。需要提供一个强制更改成员配置的接口通过它设置每个成员的成员配置列表便于从大多数成员故障中恢复。 比如只剩一个成员S1存活的时候强制更改成员配置设置成员列表为{S1}这样形成一个只有S1的成员列表让S1继续提供读写服务后续再调度其他节点通过成员变更加入。通过强制修改成员列表可以实现最大可用模式。 五 单步成员变更的工程实践 单步成员变更虽然不推荐在工程中使用这里还是总结一下单步成员变更的一些工程实践供研究讨论。 1 单步成员变更日志使用什么配置 对于单步成员变更成员变更日志是使用新成员配置 还是老成员配置Cnew呢实际上单步成员变更日志无论使用新成员配置Cold还是老成员配置Cnew都不会破坏Cold与Cnew的多数派至少有一个节点相交因此单步成员变更日志既可以使用新成员配置Cold也可以使用老成员配置Cnew两种方式各有利弊。 表4 单步成员变更日志使用老成员配置和使用新成员配置的优缺点 单步成员变更日志使用老成员配置Cold可以避免单步成员变更的正确性问题因此可以省略掉Leader上任后的no-op日志同时在增加成员时可能只需要更小的多数派集合但在减少成员时可能需要更大的多数派集合。 单步成员变更日志使用新成员配置Cnew需要Leader上任后先提交一条no-op日志以避免单步成员变更的正确性问题同时在减少成员时可能只需要更小的多数派集合但在增加成员时可能需要更大的多数派集合。 单步成员变更日志不管使用新成员配置还是老成员配置最好都同步给新老成员配置中的所有成员这样在增加成员时可以让新成员迟早收到通知在减少成员时也可以让被删除的成员收到通知而自动退出。 Raft论文中单步成员变更日志使用新成员配置Cnewetcd中单步成员变更日志使用老成员配置Cold。 2 单步成员变更日志什么时候生效 表5 单步成员变更日志的生效时机 对于单步成员变更如果成员变更日志使用新成员配置则与Joint Consensus成员变更一样Leader上开始同步成员变更日志之前就需要生效在Follower上成员变更日志持久化完成后就需要生效。如果成员变更日志使用老成员配置理论上只需要在下一次成员变更开始之前生效即可但实际为了让新加入的节点尽快开始服务一般在成员变更日志提交后就生效。 Raft论文中单步成员变更日志使用新成员配置Cnew本地持久化完成就生效etcd中单步成员变更日志使用老成员配置Cold提交后再生效。 六 总结 Raft提供了Joint Consensus成员变更和单步成员变更极大的推动了成员变更在工程中的应用。本文总结了一些Raft单步成员变更的问题以及成员变更的工程实践。Joint Consensus通用并且不容易踩坑一阶段成员变更坑比较多。工程上建议尽量使用Joint Consensus成员变更。
原文链接
本文为阿里云原创内容未经允许不得转载。