← 返回分布式系统
🗂️ 共识算法 · 复制状态机

Raft 日志复制

Raft 不止"选主"(见 basics/raft-election),更核心的是日志复制:让所有节点的"操作日志"保持一致,从而让状态机算出相同结果。这是 etcd / Consul / TiKV 的底座。

🔁复制状态机(Replicated State Machine)

核心思想:相同的初始状态 + 相同的操作序列 + 相同的处理规则 = 相同的最终状态。所以只要让所有节点"日志一致",它们的状态机自然就一致。

节点 A(Leader) 日志: 1,2,3,4 状态机 节点 B(Follower) 日志: 1,2,3,4 状态机 节点 C(Follower) 日志: 1,2,3,4 状态机 日志一致 → 三个状态机输出完全一致
🧠
一句话:Raft 不直接复制"状态",而是复制"命令日志"。状态机只是忠实地把日志一条条执行下去。这样复制问题就被简化成"日志一致性"问题。

📜日志结构

每条日志项(Log Entry)至少包含三个字段:

字段含义作用
Index日志槽位编号(从 1 递增)确定"第几条",用于匹配与对齐
Term该条由哪个任期(Leader 任期号)写入识别过期 Leader,保证单调
Command要执行的客户端命令(如 set x=3)状态机真正执行的内容
[idx1, T1, cmdA] [idx2, T1, cmdB] [idx3, T2, cmdC] [idx4, T2, cmdD]
关键约束(日志匹配原则 Log Matching):如果两个节点的日志在相同 index 处的 entry 的 term 相同,那么这两个 entry 及其之后所有 entry 都完全一致。这是 Raft 安全性的基石。

🚀正常复制流程(5 步)

客户端发命令给 Leader——如 set x=5。Leader 先把命令追加到自己日志(未提交)。
Leader 广播 AppendEntries——通过 RPC 把新 entry 发给所有 Follower。
Follower 追加并应答——若日志前置匹配,Follower 写入本地并返回成功。
Leader 多数派确认 → 提交——收到多数(含自己)成功后,Leader 标记该 entry 为 committed。
应用到状态机并响应——Leader 执行命令、返回客户端;后续 AppendEntries 中会带 commitIndex 通知 Follower 也提交执行。
Leader Follower B Follower C Follower D AppendEntries(广播日志) ACK
✅
多数派(Quorum)是关键:只要 ⌊N/2⌋+1 个节点写入成功即可提交,其余节点网络慢/挂掉也不阻塞。这样即使挂掉少数节点,系统仍可写。

🛡️安全性规则(必考)

① Leader 完整性(Leader Completeness)
一旦某 entry 在某个任期被提交,那么后续所有任期的 Leader 的日志中都必须包含这条 entry。实现靠:选举时 Leader 必须是"日志最新最全"的节点(比较最后 entry 的 term 与 index)。
② 提交规则(只提交当前任期的 entry)
Leader 只能顺着提交自己任期内的 entry;不能仅靠"多数派有旧任期的 entry"就提交旧 entry,必须等自己任期的 entry 也提交后,旧 entry 随之提交。这是为了避免 Figure 8 经典脑裂丢数据问题。
③ 状态机安全
同 index 同 term 的 entry 命令必须相同,且一旦 apply 就不可更改——保证所有节点状态机执行同一序列。
⚠️
面试高频坑:为什么不能"多数派有旧 entry 就提交"?因为旧 Leader 可能已下台,新 Leader 没这条却也凑够多数,会导致已提交数据被覆盖。Raft 用"先提交自己任期 entry 连带提交旧 entry"规避。

🔧日志冲突处理

Follower 日志可能和 Leader 不一致(网络丢包、旧 Leader 残留)。Raft 的解决方式非常"霸道但有效"。

规则:Follower 发现某 index 处 term 与 Leader 不符 → Leader 强制用自己的日志覆盖 Follower 该位置及之后的所有 entry(因为 Leader 的日志才是权威)。
Leader 如何定位冲突点

AppendEntries 带 prevLogIndex / prevLogTerm。Follower 比对:

· 不匹配 → 拒绝,并返回自己该 index 的 term;
· Leader 据此把 nextIndex 回退(一次回退一格,或跳到该 term 的起点)重试,直到匹配;
· 匹配点之后,Leader 把后续 entry 全部覆盖写入。

🔄
记忆:Follower 永远被动接受 Leader 的日志。没有"两边协商合并"——这保证了日志最终必然收敛到 Leader 的样子,简单而正确。

⚖️Raft 日志复制 vs Multi Paxos

维度RaftMulti Paxos
角色强 Leader(任期制、明确)稳定的 Leader(隐式、易变)
日志连续性日志必须连续(无空洞)允许不连续 instance
成员变更内置 joint consensus需额外方案
理解难度低(为可理解性设计)高(理论性强)
代表etcd / Consul / TiKVSpanner / 部分 ZooKeeper 早期
🎓
总结:Multi Paxos 是 Raft 的"前身/等价物",Raft 把 Leader 角色、日志连续性、成员变更都显式化,换来了好实现、好讲、好排错。

🎯面试要点速记

复制状态机:相同日志 → 相同状态机 → 相同结果,Raft 复制的是"命令日志"而非状态。
日志三要素:index、term、command;靠"日志匹配原则"保证一致性。
正常流程:Leader 追加 → 广播 → 多数 ACK → 提交 → apply。
多数派:只需 ⌊N/2⌋+1 成功即可提交,挂少数不阻塞。
安全性:Leader 完整性 + 只提交当前任期 entry(连带提交旧 entry)。
冲突:Leader 强制覆盖 Follower 不一致部分;Follower 被动接受。
🔥
必考题:"Raft 怎么保证已提交的日志不会丢?"——靠 Leader 完整性 + 选举限制(新 Leader 必须含全部已提交日志),所以新 Leader 上任后只会追加、不会覆盖已提交的 entry。