RAFT一致性算法关于成员变更(membership change)问题讨论

什么场景需要变量

正常情况下,我们最多接触的是RAFT中选举Leader,并正常提交数据的过来。并且了解leader或者follower出现故障之后,如何恢复的过程。今天讨论的场景,是关于成员变更。例如:A、B、C三台机器,A机器负载比较高,需要更强的A1机器顶上。这时候,需要用到成员变更。原来的集群A、B,C,换成A1,B,C。

变成的方式

方案一:停掉A,增加A1。
方案二:增加A1,停掉A。
方案三:直接对成员更新其配置(例如需要在原来三个集群中,增加两个成员)

存在的问题

以上两种方式,都可以达到更换机器的目的。但存在的问题也比较明显,无论是方案一还是二,都存在一个可用性问题。

对于方案一

先停掉A的时候,如果B或者C机器中任何一台出现故障,则集群不可用。相当于此时为单点。

对于方案二

增加了机器A1到集群中,此时,A、B、C中的Leader需要向A1同步数据较长时间,A1这段时间无法提交日志。如果此时,A、B、C中任何一台机器坏掉,则此时,集群因为是四台机器,需要三台机器达成共识才能对外服务。假设此时B为主,A挂掉,如果客户端提交一个日志,则B必须把日志提交到A1,B,C均成功才能认为群集达成共识。而此时A1的日志还没有顺序写到最新,所以无法响应最新的日志写入,所以集群此时不可用。

优化点

主要是A1机器与A同步数据这段时间,存在系统的单点问题。如果同步数据的时间能够缩短,那系统的单点时间能够更短,让系统更加健壮。
RAFT官方论文里面提出:可以先让A1切换成Learner角色,这个角色只会从Leader同步数据,但不会在这个集群中参与选举。那么,原来的三个集群还是不变,只是A1会先从Leader处同步数据。等到同步差不多一致的时候,再做A1的增加,做A的减员。这样来完成成员变更。

对于方案三

如果直接变更成员的配置信息,是否会导致什么问题?
RAFT一致性算法关于成员变更(membership change)问题讨论
如上图所示,对于在更新配置的某一时刻,假设有5个服务。原来服务1,2,3。增加了两个服务4,5。现在假设某一时刻,3服务先更新了新的配置C_new,而原来1,2还是老配置C_old。会存在脑裂问题。对于3,4,5而言,他们都是新配置,他们可能会选举出一个主。对于1,2而言,他们两个认为他们还是在1,2,3的集群里面,则1,2中会选举出一个新的主出来,这时候,会出现两个主的情况,这对RAFT集群中不可接受的。

终极解决方案

对于Learner方案,虽然也是一个解决办法。但始终无法杜绝可用性问题,有一小段时间系统存在可用性问题。那究竟有没有一个完全无损的成员变更方案?
RAFT官方论文中提到了joint consensus方案来解决问题。
借用一下官方论文中的图。
RAFT一致性算法关于成员变更(membership change)问题讨论
主要流程是分两阶段修改配置。joint consensus在更新过程中,完全不影响集群的可用性与对外服务。
步骤1:当leader收到新配置C_new的时候,他首先是把它与老配置做并集,生成一个C_old_new配置。Leader会将C_old_new发到所有成员。直到大多数已经成功收到这个配置的日志。
步骤2:如果大多数收到C_old_new之后,Leader再把C_new发到集群中。

分析:从上面的图中,我们可以看到,这里有几个关键节点。
1)当C_old_new大多数提交之前,C_old可以单独选主,这不会有问题。
2)当C_old_new大多数提交之后,C_old配置的机器已经无法选Leader。只有C_old_new配置生效的可以选举出Leader。
3)当C_new大多数生效之后,只有C_new配置的机器可以选举出新的Leader。此时,C_old配置上的机器,可以安全的进行下线了。

反思

当遇到问题的时候,如果一时半会没搞清楚,则需要回到源头去找答案。对于一些理论性较强的问题,需要能够找到问题的源头,知道业界当前对这个问题的最权威解读。顺着这个思路去找,你会发现,所有网上的资源,无非都是从RAFT的原始论文中得出来的。你需要真正了解问题,则需要去阅读RAFT的论文。
另外,阅读论文的时候,建议最好直接阅读原文。当时自己找过中文翻译文章,但读起来的论证思路与英文原文,差别太大。最后还是读英文原文才比较好的理解了作者解决问题的思路。