Raft共识算法PartB:Log Replication

151 阅读5分钟

本文接上一篇文章:Raft共识算法PartA:Leader Election And HeartBeat

当Leader被选出来后,它们就需要开始提供服务了。

Raft共识算法是通过复制日志的方式,实现容错的。Raft 会一直维护着以下的特性,这些特性也同时构成日志匹配特性(Log Matching Property)

  • 如果不同日志中的两个条目有着相同的索引和任期值,那么它们就存储着相同的命令
  • 如果不同日志中的两个条目有着相同的索引和任期值,那么他们之前的所有日志条目也都相同

预写日志

当Leader收到一条读/写请求,便会产生一条预写日志。当日志被大多数服务复制成功后,Leader才会提交该日志。 日志的组织形式如下。

图片.png

观察可知,通过任期Term和下标Index可以让我们唯一确定一条请求的日志。

二阶段提交

产生一条日志后,这条日志并不能马上确认为已提交的日志。其中涉及到两个阶段。

日志复制阶段

预写日志被大多数服务成功复制后,Leader确认

图片.png

日志提交

Leader提交该日志,并告诉Follower们,我提交了这条日志,你们也可以提交这条日志了。

图片.png

一致性检查

心跳不是特殊的日志复制,而是在心跳的过程中会伴随着一致性检查和日志复制、日志提交等一系列操作。 在Leader发送给Follower的参数中,会存在PrevLogIndex与PreLogTerm俩个参数。Leader只要认为,当前我要复制的日志的前一个,在Follower当中存在且正确,那么它前面的日志都是正确的。

Leader强制Follower复制日志确保一致性

当发现一致性检查不通过时,Leader会尝试退一个日志,然后在下一次心跳尝试将日志复制过去。

为了使 follower 的日志跟自己(leader)一致,leader 必须找到两者达成一致的最大的日志条目索引,删除 follower 日志中从那个索引之后的所有日志条目,并且将自己那个索引之后的所有日志条目发送给 follower。所有的这些操作都发生在 AppendEntries RPCs 的一致性检查的回复中。

leader 维护着一个针对每一个 follower 的 nextIndex,这个 nextIndex 代表的就是 leader 要发送给 follower 的下一个日志条目的索引。

如果一个 follower 的日志跟 leader 的是不一致的,那么下一次的一致性检查就会失败。在被 follower 拒绝之后,leader 对 nextIndex 进行减 1,然后重试。最终 nextIndex 会在某个位置满足 leader 和 follower 在该位置及之前的日志是一致的,此时,就会成功,将 follower 跟 leader 冲突的日志条目全部删除然后追加 leader 中的日志条目(需要的话)

一旦发送日志成功,follower 的日志就和 leader 的一致了,并且在该任期接下来的时间里都保持一致。

选举限制

在任何基于 leader 的共识算法中,leader 最终都必须存储所有已经提交的日志条目。Raft 使用了一种更加简单的方法,它可以保证新 leader 在当选时就包含了之前所有任期中已经提交的日志条目,根本就不需要再传送这些日志条目给新的 leader。这就意味着日志条目的传送只有一个方向,那就是从 leader 到 follower,leader 从来不会覆盖本地日志中已有的日志。

Raft 采用投票的方式来保证一个 candidate 只有拥有之前所有任期中已经提交的日志条目之后,才有可能赢得选举。一个 candidate 如果想要被选为 leader,那它就必须跟集群中超过半数的节点进行通信,这就意味这些节点中至少一个包含了所有已经提交的日志条目。如果 candidate 的日志至少跟过半的服务器节点一样新,那么它就一定包含了所有以及提交的日志条目,一旦有投票者自己的日志比 candidate 的还新,那么这个投票者就会拒绝该投票,该 candidate 也就不会赢得选举。

所谓 “” :
Raft 通过比较两份日志中的最后一条日志条目的索引和任期号来定义谁的日志更新。

  • 如果两份日志最后条目的任期号不同,那么任期号大的日志更新
  • 如果两份日志最后条目的任期号相同,那么谁的日志更长,谁就更新

提交之前任期内的日志条目

一旦当前任期内的某个日志条目以及存储到过半的服务器节点上,leader 就知道该日志可以被提交了。如果这个 leader 在提交某个日志条目之前崩溃了,以后的 leader 会尝试完成该日志条目的复制。然而,如果是之前任期内的某个日志条目已经存储到了过半的服务器节点上了,新任期内的 leader 也无法立即断定该日志条目已经被提交了

v2-f17b18f2bb953847cdb610298a1cc51c_1440w.webp

为了解决图 8 中描述的问题,Raft 永远不会通过计算副本数目的方式来提交之前任期内的日志条目。只有 leader 当期内的日志条目才通过计算副本数目的方式来提交。一旦当前任期内的某个日志条目以这种方式被提交(如图 8 中的 e),那么由于日志匹配特性(Log Matching),之前的所有日志条目也会被间接地提交。在某些情况下,leader 可以安全地断定一个老的日志条目已经被提交(例如,如果该条目已经被存储到每一个节点上了)。但是 Raft 为了简化问题,采取了上述描述的更加保守的方法。

参考资料: