<\![CDATA[

一、为什么需要分布式一致性?

在分布式系统中,数据存储在多台机器上,如何保证所有副本在同一时刻看到相同的状态,是一个核心挑战。当某个节点宕机、网络分区或消息延迟时,系统仍能正确运转——这就是分布式一致性协议要解决的问题。

常见的一致性级别包括:强一致性(线性一致性)、顺序一致性、因果一致性和最终一致性。Raft 协议提供的是强一致性保证,确保客户端读到的总是最新已提交的数据。

二、Raft 的设计哲学:可理解性优先

Raft 诞生于 2014 年,由 Diego Ongaro 和 John Ousterhout 在论文《In Search of an Understandable Consensus Algorithm》中提出。相比 Paxos 的"难以理解",Raft 将共识问题分解为三个相对独立的子问题:

  1. Leader 选举:选出集群的"话事人"
  2. 日志复制:确保所有节点拥有相同的操作序列
  3. 安全性:保证已提交的日志不会被覆盖

通过"状态机复制"(Replicated State Machine)模型,Raft 将业务逻辑抽象为确定性状态机,只要输入序列一致,所有节点的最终状态就必然一致。

三、Raft 核心概念与术语

3.1 节点角色

Raft 将节点分为三种角色:

  • Leader(领导者):处理所有客户端请求,管理日志复制,定期发送心跳维持权威
  • Follower(跟随者):被动响应 Leader 的心跳和日志追加请求
  • Candidate(候选人):选举临时状态,发起投票请求以竞争 Leader

3.2 任期(Term)机制

时间被划分为连续的任期(Term),每个 Term 是一个逻辑时钟,单调递增。Term 的作用是:

  • 标识 Leader 的合法性(同一 Term 只有一个 Leader)
  • 检测过期信息:节点收到旧 Term 的消息时直接拒绝
  • 驱动角色转换:发现更高 Term 时自动降级为 Follower

3.3 四大核心 RPC

RPC 类型发起方用途
RequestVoteCandidate竞选 Leader,请求选票
AppendEntriesLeader日志复制 心跳(空条目即为心跳)
InstallSnapshotLeader快照传输,用于追赶落后太多的 Follower
TimeoutNowLeader主动触发转移(可选实现)

四、Leader 选举详解

4.1 选举触发条件

Follower 会在选举超时(通常 150-300ms 随机化)内未收到 Leader 心跳时发起选举。随机超时机制是为了避免多个节点同时发起选举导致"选票分裂"(Split Vote)。

4.2 选举流程

  1. Follower 自增 Term,转为 Candidate
  2. 投给自己一票,向所有节点广播 RequestVote
  3. 收到大多数(N/2 1)选票后成为 Leader
  4. 立即发送心跳,阻止其他节点发起新选举

4.3 安全性约束:日志至少一样新

RequestVote RPC 包含候选人最后日志的 Term 和 Index。选民会检查:

  • 候选人最后日志 Term < 我的最后日志 Term → 拒绝
  • 候选人最后日志 Term == 我的,但 Index < 我的 → 拒绝

这保证了当选 Leader 一定包含所有已提交日志。

五、日志复制机制

5.1 正常流程

客户端请求到达 Leader 后:

  1. 将操作追加为日志条目(未提交状态)
  2. 通过 AppendEntries RPC 并行发送给所有 Follower
  3. 大多数节点确认后,Leader 提交该条目
  4. 提交后应用到状态机,返回结果给客户端
  5. 下次心跳通知 Follower 提交

5.2 日志一致性检查与修复

AppendEntries 包含前一条日志的 Term 和 Index 用于一致性检查。当 Follower 在对应位置找不到匹配的条目时,Leader 会递减 NextIndex逐步后退,直到找到一致点,然后覆盖后续日志。这种"回退"机制在日志分叉时尤为重要。

5.3 提交规则:不能提交之前任期的日志

这里有一个极易踩坑的细节:Leader 不能直接提交之前 Term 的日志条目。原因是 Leader 的提交是基于"当前 Term 有条目被确认"来递增 commitIndex 的。如果旧 Term 的条目被直接提交,在后续复制时可能被覆盖(例如前任 Leader 的未提交条目被新 Leader 覆盖)。

正确做法:Leader 在当前 Term 有新条目被大多数确认后,就可以安全地提交当前及之前所有条目(因为当前 Term 条目的提交隐含保证了之前所有条目的一致性)。

六、安全性保证

6.1 Election Restriction(选举限制)

已如前述,候选人日志必须"至少与投票者一样新"。

6.2 Commitment Rule(提交规则)

如 5.3 节所述,通过间接提交保证安全性。

6.3 Leader Completeness Property

某个日志条目在某个 Term 被提交,则该条目必然存在于所有更高 Term 的 Leader 中。这是 Raft 最核心的安全定理。

七、快照与日志压缩

长期运行的系统中,日志无限增长会带来内存和恢复时间的压力。Raft 通过快照(Snapshot)机制解决:

  • 当日志达到阈值时,Leader 生成快照,包含当前状态机状态和最后包含的日志的 Term/Index
  • 旧日志条目可以被安全删除
  • 当 Follower 落后太多时,Leader 通过 InstallSnapshot RPC 发送快照

快照实现的关键是保证快照最后包含元数据的正确性,以及处理快照传输期间的并发修改。

八、成员变更(Joint Consensus)

集群不能停机扩容缩容。Raft 使用两阶段成员变更(Joint Consensus):

  1. 切换到"联合配置"(Cold ∪ Cnew),所有决策需同时获得旧配置和新配置的大多数同意
  2. 联合配置提交后,切换到新配置 Cnew
    1. 这避免了在过渡期间两个配置各自形成多数派导致脑裂的问题。

      九、工程实践中的关键细节

      9.1 Pre-Vote 协议

      一个与网络隔离的节点恢复后会不断发起选举(Term 递增),但无法获得选票,导致集群 Term 无限增长。Pre-Vote 协议要求候选人先发起一轮"预投票",只有在获得大多数同意后才会自增 Term 发起正式选举。

      9.2 Checkquorum 与 Lease Read

      Leader 需要确认自己仍是 Leader(没有被网络分区隔离)。Lease Read 通过心跳确认 时钟偏移保证,在租约期内直接读取本地状态,无需走日志提交。

      9.3 Leadership Transfer(领导权转移)

      在节点计划维护前或均衡负载时,主动将 Leader 转移给指定节点。步骤为:
      ① Leader 暂停服务
      ② 发送 TimeoutNow给目标节点
      ③ 目标节点立即发起选举
      ④ 由于日志最新,该节点必然当选

      9.4 批量优化与流水线

      生产环境中,连续日志条目可以批量发送,减少网络往返。AppendEntries 处理采用流水线:不等前一个 RPC 返回就发送下一个,提高吞吐量。

      十、Raft 实现框架对比

      项目语言特点
      etcd/raftGo生产级,Kubernetes 核心组件 etcd 的底层共识
      TiKV (raft-rs)Rust高性能,PingCAP 出品,async/await
      braftC 百度出品,支持多组 Raft
      jraftJavaApache 生态集成
      SOFAJRaftJava蚂蚁金服出品,多组、全功能

      十一、总结

      Raft 通过"分解问题 可理解性优先"的设计,成为目前最广泛使用的分布式一致性协议。从 etcd 到 TiKV、从 Consul 到 CockroachDB,Raft 已是构建可靠分布式系统的基础设施。

      理解 Raft 不仅是面试加分项,更是设计高可用系统的必备内功。核心记住三件事:选举靠超时 多数票,日志靠 AppendEntries 一致性检查,安全性靠"当前 Term 提交带动历史提交"。


      参考资料:Ongaro

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部