1770 words
9 minutes
Raft 协议:Leader 选举与日志复制

Raft协议是用来解决什么问题的#

Raft 协议的核心目标是解决分布式系统中的一致性(Consensus)问题。

在分布式环境下,由于网络延迟、断网或硬件故障,让多台机器达成“共识”是非常困难的。Raft 的出现就是为了让一个集群在部分节点出错的情况下,依然能像单机一样提供可靠、准确的服务。

简单来说就是一个服务器是容易出错的,但是我们可以使用多个服务器协同,让使用者认为这个多个服务器组成的集群是一个永远不会出错的单体服务器。

Raft协议的基本概念#

节点状态(身份)#

  • Follower(跟随者):标准状态。不发送请求,仅响应来自 Leader 和 Candidate 的消息。
  • Candidate(候选人):竞选状态。Follower 在选举超时后转为此状态,发起投票请求。
  • Leader(领导者):权威状态。负责接收客户端指令,并管理日志的全局同步。

正常运行#

在集群正常运行期间,Leader 会通过定期发送 AppendEntries RPC(心跳包)来维持其权威地位。Follower 每当接收到心跳时,都会 重置自身的选举计时器,只要心跳信号保持不断,它们就会持续处于服从状态而不会发起新的选举。与此同时,为了确保指令执行的严格顺序性,所有数据的修改请求都必须统一由 Leader 作为唯一入口进行处理。

故障切换#

当集群中的 Follower 在其 随机超时时间内(150ms - 300ms) 未收到来自 Leader 的心跳时,会判定原 Leader 故障并 触发选举流程。该 Follower 会立即自增任期号(Term)并转变为 Candidate 状态,随后向集群内其他节点发送投票请求。若该 Candidate 获得 超过半数(N/2+1)节点 的赞成票,则正式升级为新 Leader。新 Leader 产生后会立即向全网广播心跳包,其他节点在收到更高任期的心跳后,会停止竞选并 退回 Follower 状态,从而完成集群权力的平滑交接。

数据同步#

当 Leader 接收到写请求后,会将其记录为本地日志并 同步发送给所有 Follower 进行复制。一旦 Leader 收到 超过半数节点的成功响应,该日志便进入“已提交”状态。此时,Leader 将指令应用到本地状态机执行,并利用后续心跳通知 Follower 完成各自状态机的更新,从而实现全集群的数据强一致性。

Leader选举过程#

在拥有一个 Leader (L) 和三个 Follower (F1, F2, F3) 的四节点集群中,当任期为 Term=1Term=1 且 L 在 0ms 瞬间宕机时,集群将进入以下自动选举流程:

1. 触发竞选与任期更迭#

由于心跳停止,各 Follower 开始倒计时各自的随机选举超时时间(假设 F1 为 160ms,F2 为 200ms,F3 为 250ms)。当计时达到 160ms 时,F1 率先超时并脱离 Follower 状态,将其本地任期自增为 Term=2Term=2,转变为 Candidate(候选人)。F1 在投给自己一票后,立即向 F2 和 F3 发送包含最新任期号和日志信息的 RequestVote RPC 请求。

2. 投票干预与计时器重置#

在 190ms 时,F2 接收到了来自 F1 的投票请求。此时,尽管 F2 原定的 200ms 超时点即将到来,但由于收到的请求任期号(Term=2Term=2)大于其本地任期(Term=1Term=1),F2 会立即同步更新自己的任期为 2。在通过日志一致性检查后,F2 将选票投给 F1。关键逻辑在于:根据 Raft 协议,一旦 Follower 授予了投票,就必须重置其选举计时器。 因此,F2 原定在 200ms 触发的超时计划被取消,其任期将维持在 2 而不会自增为 3,且不再尝试发起新的竞选。

3. 达成共识与建立权威#

至 230ms 时,Candidate F1 已经收到了来自自身、F2 以及 F3 的投票(共 3 票)。根据法定人数公式 Quorum=⌊4/2⌋+1=3Quorum = \lfloor 4/2 \rfloor + 1 = 3,F1 获得的票数已达到过半数要求,正式升级为新任 Leader。此时,F1 立即向全网发送第一轮心跳包,F2 和 F3 收到心跳后确认 F1 的合法地位,并继续作为 Follower 运行。

4. 旧 Leader 恢复后的处理#

如果旧 Leader (L) 稍后重新上线,它仍持有过时的 Term=1Term=1。当它接收到新 Leader F1 发送的 Term=2Term=2 心跳或响应时,会通过任期号对比(1<21 < 2)立刻意识到自己已经过时。随后,旧 Leader 会执行“身份回退”逻辑,自动退化为 Follower 并开始从新 Leader 处同步缺失的日志条目。

数据同步过程#

1. 客户端请求与本地追加#

当客户端向集群发起写操作请求时,该请求必须由当前 Leader 接收。Leader 首先将该指令封装成一个 日志条目(Log Entry),并将其追加到自己的本地日志文件中。此时,该日志条目处于 未提交(Uncommitted) 状态,尚未对系统状态产生实际影响。

2. 日志并行分发#

Leader 在完成本地追加后,会立即通过 AppendEntries RPC(追加条目请求)将该日志条目并行发送给集群中的所有 Follower。这个 RPC 消息不仅包含新的日志内容,还携带了当前 Leader 的任期号(Term)以及前一条日志的索引和任期,用于 Follower 进行一致性检查。

3. Follower 接收与回复#

Follower 在收到 Leader 的分发请求后,会验证日志的合法性。如果日志顺序正确且任期匹配,Follower 会将该条目追加到自己的本地日志中,并向 Leader 返回一个成功的确认响应(ACK)。此时,Follower 端的日志同样处于“未提交”状态。

4. 达成过半数共识#

Leader 会持续收集来自各个 Follower 的确认。一旦 Leader 确认该日志条目已被 过半数 的节点成功接收(包括 Leader 自身,公式为 ⌊N/2⌋+1\lfloor N/2 \rfloor + 1),该日志就被认为达到了“安全”状态。

5. Leader 正式提交与应用#

达到安全状态后,Leader 将该日志条目的状态标记为“已提交(Committed)”,随后将其应用(Apply)到本地的状态机(State Machine)中。状态机执行具体的指令,并产生最终的执行结果。

6. 响应客户端与通知提交#

Leader 在完成本地应用后,会将执行结果返回给客户端。同时,在下一次心跳或后续的日志分发请求中,Leader 会携带当前的“已提交索引(Commit Index)”。Follower 收到该索引后,得知此前的日志已经过半数通过,于是也会将对应的日志条目应用到自己的本地状态机。

Raft 协议:Leader 选举与日志复制
https://blog.emilejones.top/posts/systems/raft协议/
Author
EmileJones
Published at
2026-03-23
License
CC BY-NC-SA 4.0
评论