Raft membership change

来源:互联网 发布:阿里 知乎 编辑:程序博客网 时间:2024/06/16 07:13
Raft membership change

要保证成员变更过程中的safety,就要保证在任何时候,都不会出现双主。

如果一次成员变更中,将成员组立刻切换为新的成员组,那么就会因为各个成员之间不能同时生效而导致双主,如图。

disjoint-majorities

从(S1,S2,S3)变为(S1,S2,S3,S4,S5)的变更中,因为各个成员上变更生效时间不同,可能导致在中间某个时刻,出现两个disjoint majorities,两个多数派能够分别选主并服务客户端,导致一致性无法保证。

为了保证safety,常规的解法是使用两个阶段。例如,在Viewstamped Replication协议里,成员变更先停止旧的成员组,然后启用新的成员组,但是这样导致中间会有停服务的问题。

Raft里采用的两阶段方案是,集群先进入一个称之为Joint Consensus的过渡状态,等过渡状态commit了,再只使用新的成员组。在Joint Consensus中,不管是日志复制(客户端提交的普通日志)还是选主,都要在新旧两个成员组C_old、C_new内分别形成多数派。

成员变更命令通过成员变更日志作为载体复制到其他副本。一个副本只要收到了成员变更日志,之后的日志就立刻使用新的成员组开始工作,不管这条成员变更是不是commit了。也就是说,leader要用两个多数派C_old,new同时满足来commit这条成员变更日志。

因为有Joint Consensus,所以在C_old,new commit之前,C_new无法单方面确认任何事情。那么,在C_old,new commit之前如果leader宕机了呢?这种情况下,C_old或者C_old,new可能选出新的leader(取决于新主上是否有C_old,new这条日志)。同样,C_new仍然无法单方面确认任何事情。

一旦C_old,new commit了,那么即使leader宕机,选出来的新leader也一定有C_old,new这条日志了(选主的约束:Leader拥有全部commit的日志)。此时,leader发起一个成员变更,将状态转为只使用C_new的状态(这条日志也记为C_new)。直到C_new被commit了,C_old便不再被需要。成员变更的两个阶段就完成了。
这个过程如图示:

joint_consensus

成员变更保证safety,要遵循的原则是:在任何时候都不允许C_old和C_new同时可以单方面做决定。例如图中上半部分,C_old和C_new分别可以单方面做决定的时间段是没有重叠的,中间使用C_old,new过渡。