RAFT中的COMMIT
commit意味着什么
对于Server,意味着代码被执行。
对于Leader,意味着大多数Server已经执行。
需要明确的概念
我们首先需要来看看RAFT算法的一些定义。
这里面有两个需要注意的地方:
- commit是已经Server执行的代码。每一个server都有一个commitIndex作为commit的记录,也就是执行过的代码的Index。leader的commitIndex又叫leaderCommit。需要注意的是:Leader的commitIndex是当受到大多数节点成功日志复制后,才会更新的 。
- 每一个Server会维持一个LastLogIndex和LastLogTerm,作为与其他节点交流时,进行新旧比较的依据。
投票的过程
当Candidate发送给Follower RequestVote RPC请求后,Follower只会比较两个参数,一个是LastLogIndex,一个是LastLogTerm。只要这两个数据大于本地的数据,那Follower就会给对方投票。
问题来了!
接下来我们要分析这里出现的情况,在已有的算法基础上,会不会有问题呢?
-
在a阶段,S1作为Leader本地Commit了Log,并通过AppendEntries将信息发送给其他Server,其中S2收到信息并在本地Commit。
-
在b阶段,S1掉线,S5成为了Leader,这里接受到(S3,S4,S5)三者的投票。S5在任期3,Commit了Log后掉线。
-
在c阶段,S1重新进入集群,并被选为Leader,S3接受到LeaderS1的信息,在本地Commit了任期2的日志。
-
在d阶段,S5重新被选为Leader,这里为什么S5会被选了Leader呢?这是因为在进行投票时,Follower只会比较LastLogIndex和LastLogTerm,对于S5,这两个参数分别为2,和3。这样的话,他就可能会接收S2,S3,S4,S5四个人的投票。
而一旦S5成为了Leader,现在所有其他节点就会以S5任期3的Log为准。之前已经被大多数节点Commit的Log就会被覆盖。这就会导致不一致性,前面已经被大多数节点接收的内容,竟然会被覆盖?这也是问题所在。
那如何修复Raft算法呢? Raft在这里要求,Leader不允许Commit之前任期的Log。如果Leader想要执行之前任期的Log,他需要在新的任期里来执行。事实上,这个时候S1在任期2里面的日志,在他的记录里是没有Commit的,因此他很容易识别这个问题。
- 图中还有一个e阶段,这是正常的情况下,任期2时Leader就将大多数日志复制给别的节点,这个时候S5也不会被选为Leader,就不会出现之后的覆盖问题。