跳到主要内容
约 6 分钟4 张卡

Raft

Raft 用选举和日志复制维护确定的状态机执行顺序。本文讲解任期、提交、线性读取、快照和成员变更。

1. 面试核心​

先记住这几条,再读正文
  • 任期与投票。 节点每个 term 最多投一票,currentTerm 与 votedFor 需持久化。
  • 日志新旧比较。 日志新旧先比较最后条目的 term,再比较 index。卡 · 角色与任期
  • 提交规则。 Leader 仅按多数副本数直接提交当前 term 条目。卡 · 当前任期与旧任期条目
  • 线性一致读。 线性读需确认领导权,并等待状态机应用到安全读取位置。卡 · 读取屏障
  • 成员变更。 joint consensus 同时要求新旧配置的多数,learner 追赶时不计票。卡 · Learner 与多数门槛

2. 角色与任期​

Follower 响应复制和投票请求。Candidate 在选举超时后发起新任期。Leader 接收命令、复制日志并发送心跳。

节点收到更大 term 后更新任期,并退回 Follower。同任期内只允许一票,避免两个候选者各获多数。

2.1 持久状态与故障模型​

currentTerm、votedFor 和日志需在响应相关 RPC 前持久化。节点若重启后忘记投票,就会在同任期重复投票。副本若确认后丢失日志,也会破坏恢复前提。

Raft 处理崩溃、重启、延迟、丢包和分区。协议假设节点按规则执行。恶意伪造投票和日志属于拜占庭故障,超出普通 Raft 的保证。

固定配置下,2f+1 个投票节点可容忍 f 个不可用。继续推进还需多数节点互通并获得足够稳定的选举时机。

3. 领导者选举​

Follower 选举超时后增加 term,给自己投票并请求其他票。候选者取得多数后成为 Leader。随机超时降低同时竞选的机会。

投票先比较最后日志条目的 term。term 相同再比较 index。较短日志若最后 term 更大,仍算更新。

选举超时应明显长于正常通信耗时。延迟持续过大时,频繁竞选会降低可用性。安全规则仍需保持。

4. 日志复制​

日志条目包含 index、term 和命令。AppendEntries 携带前一个条目的 index 与 term。

Follower 检查前缀是否匹配。失败时 Leader 回退 nextIndex,重新寻找共同前缀。相同 index 的 term 冲突时,删除冲突条目及其后缀。已匹配的日志继续保留。

日志匹配性质保证相同位置与任期对应同一前缀。

5. 提交​

Leader 跟踪各副本的持久日志位置。当前 term 的条目复制到多数后,可推进 commitIndex。状态机按 index 应用,再返回执行结果。

Follower 根据 Leader 的提交位置推进本地提交。commitIndex 表示已知可提交位置,lastApplied 表示实际应用位置。

5.1 当前任期与旧任期条目​

旧 term 条目即使已复制到多数,也不能仅据此直接提交。其他节点可拥有更高任期的不同后缀,并在后续选举中胜出。

Leader 应先让一条当前 term 条目获得多数。它可是一条业务命令,也可以是 no-op。该条目提交后,之前的日志前缀随之提交。

选举的新旧比较、日志匹配和当前任期提交共同保证安全。多数交集本身只保证有共同节点。共同节点仍按日志新旧规则决定投票。

6. 客户端重试​

命令已提交但响应丢失时,客户端会重试。日志中的第二条命令仍是一次独立输入。

状态机可保存 client ID、请求序号和结果。重复请求返回原结果。外部邮件或 HTTP 操作还需独立幂等,具体恢复见分布式流程恢复。

7. 线性一致读​

Leader 失联后,其他节点可以选出新 Leader。旧 Leader 在获知更高任期前仍会自认有效。本地直接读会返回过期状态。

7.1 读取屏障​

新 Leader 先提交当前 term 的条目,确认已提交前缀。论文使用启动任期时的 no-op。

一次安全读取还需确认当前领导权。ReadIndex 类流程与多数派交互,并得到安全读位置。状态机等待 lastApplied 达到该位置后,再执行读取。

Follower 也可向 Leader 获取安全读位置,等待本地应用后读取。只看自己的 term 或历史 commitIndex,缺少本次读取的确认。

租约读可减少往返,但安全性依赖时钟与租约假设。

8. 快照​

快照保存已应用状态与最后包含的 index、term。已压缩的日志前缀由快照代替。过于落后的 Follower 安装快照,再复制后续日志。

快照还需包含生效的成员配置。状态与位置应一致且可持久恢复。共识副本之外的历史备份仍用于逻辑损坏恢复。

9. 成员变更​

直接替换成员列表会让新旧多数互不相交。joint consensus 在过渡期同时取得两套配置的多数。之后再提交最终新配置。

部分实现采用经过证明的单成员变更协议。它与联合配置是不同实现路径,应按具体协议操作。

9.1 Learner 与多数门槛​

三投票节点需要两票,四投票节点需要三票。若已有一个节点故障,再加入未就绪的投票节点,就只剩两票可用。

etcd 3.6 支持以 learner 加入新节点。learner 接收日志,但不参与投票,也不提高多数门槛。追平并满足提升检查后,再转成投票成员。

成员变更通过现有共识完成。丢失多数后,普通加节点操作无法恢复法定票数。

10. 常见问题​

10.1 三节点集群的故障余量​

三节点失去两台后,剩余一台无法取得多数。线性写入停止,以避免两个分区各自推进。允许陈旧读的接口可以另行提供服务。

10.2 心跳与空 AppendEntries​

11 · 面试题卡片​

11.1 Raft 如何保证新 Leader 包含已经提交的日志​

出现公司:字节跳动

考察重点

  • term、多数投票和候选人日志新旧比较。
  • AppendEntries 前缀匹配与冲突修复。
  • 当前 term 提交规则、读取与成员变更。

相关内容:角色与任期至成员变更。

参考回答

新 Leader 保留已提交日志,依赖选举限制和当前任期提交规则。

每个节点同任期只投一票。候选日志先比较最后 term,再比较 index。日志匹配保证共有条目前缀一致。

当前 term 条目由多数持久化后才直接提交。结合这些性质,可证明后续 Leader 都包含该前缀。旧 term 条目随新任期条目间接提交。仅说明两个多数相交,还不足以完成证明。

11.2 旧任期日志已经复制到多数节点,Leader 为什么还要提交当前任期条目?​

出现公司:暂无已核验的公开来源(本站自测)

考察重点:选举的新旧规则与间接提交。

相关内容:当前任期与旧任期条目。

参考回答

旧任期条目的多数副本数不足以直接证明安全。

后续候选者仍可凭更高任期后缀通过新旧比较。当前任期条目得到多数后,协议才能保证后续 Leader 保留它。此前日志随该条目一起提交。

11.3 Raft Leader 执行线性一致读时,需要哪些确认?​

出现公司:暂无已核验的公开来源(本站自测)

考察重点:当前任期提交、领导权与应用进度。

相关内容:读取屏障。

参考回答

Leader 需确认已提交前缀、当前领导权和状态机进度。

新任期先提交当前 term 条目,例如 no-op。读取再经多数派确认得到安全位置。lastApplied 达到该位置后,才读取本地状态。租约优化还需额外时钟假设。

11.4 三节点集群已有一台故障,为什么新增投票节点可能使集群停写?​

出现公司:暂无已核验的公开来源(本站自测)

考察重点:成员数量、多数门槛与 learner。

相关内容:Learner 与多数门槛。

参考回答

成员数变为四个后,多数门槛从两票升到三票。

新节点尚未就绪时,只有原来的两票可用。learner 追赶期间不计票,因此保留原门槛。追平后按协议提升为投票成员。

12. 参考资料​