高 困难
Raft协议#
一句话答案#
Raft 将分布式共识分解为 Leader 选举、日志复制、安全性三个子问题,通过”多数派同意即提交 + term 递增防脑裂”保证一致性,比 Paxos 更易理解和实现。
核心要点
Raft 三大子问题#
1. Leader Election —— 谁来当老大?
2. Log Replication —— 老大怎么让大家保持一致?
3. Safety —— 怎么保证已提交的数据不丢?plaintext1. Leader 选举#
节点三种角色:
- Leader:处理所有写请求,向 Follower 复制日志
- Follower:接受 Leader 的日志复制,响应读请求(部分实现)
- Candidate:正在竞选 Leader 的节点
选举流程:
1. Follower 在 election timeout(随机 150ms~300ms)内没收到 Leader 心跳
2. 转为 Candidate,term+1,投自己一票
3. 向所有节点发送 RequestVote RPC
4. 收到多数票 → 成为 Leader,立即发送心跳宣告
├── 同一 term 内每个节点只投一票(先到先得)
└── 投票条件:Candidate 的日志至少和自己一样新(term 更大,或 term 相同但 logIndex 更大)
5. 如果没拿到多数票 → 等待随机超时后重新发起plaintext为什么用随机超时? 避免多个节点同时发起选举导致反复平票(split vote)。
2. 日志复制#
流程:
1. 客户端发送写请求到 Leader
2. Leader 将命令追加到自己的日志(uncommitted)
3. Leader 并行发送 AppendEntries RPC 给所有 Follower
4. Follower 验证日志一致性(prevLogIndex + prevLogTerm 匹配)
├── 匹配 → 追加日志,返回 success
└── 不匹配 → 返回 fail,Leader 递减 nextIndex 重试
5. Leader 收到多数 Follower 的 success → 提交日志(committed)
6. Leader 返回客户端成功
7. 下一次心跳/AppendEntries 通知 Follower 提交已 committed 的日志plaintext日志匹配属性(Log Matching Property):
- 如果两个节点的日志在相同 index 有相同 term → 该 index 及之前的所有日志完全相同
- 这保证了 Leader 可以通过逐步回退 nextIndex 来对齐 Follower 的日志
3. 安全性保证#
三个关键保证:
| 保证 | 含义 |
|---|---|
| Election Safety | 同一 term 内最多一个 Leader |
| Leader Completeness | 已提交的日志一定存在于后续所有 Leader 中 |
| State Machine Safety | 所有节点按相同顺序执行相同的已提交日志 |
Leader Completeness 怎么保证?
- 投票时检查 Candidate 日志是否”足够新”
- Candidate 的最后一条日志 term 更大,或 term 相同 index 更大
- → 拥有最新已提交日志的节点才可能当选
脑裂防护#
场景:网络分区导致 Leader A 和 Follower B/C 分到不同网络
分区 1: [A(Leader, term=3)]
分区 2: [B, C] → B 超时发起选举,term=4,C 投票 → B 成为新 Leader
此时两个 "Leader":A(term=3) 和 B(term=4)
A 无法获得多数确认(只有自己)→ 任何写入都不会被提交
B 有多数节点 → 正常工作
网络恢复后:
A 收到 term=4 的消息 → 发现自己 term 过期 → 自动降级为 Follower
A 的未提交日志被 B 的日志覆盖
关键:term 递增 + 多数派 = 自动解决脑裂,不会出现两个有效 Leaderplaintext日志压缩(Snapshot)#
- 日志无限增长 → 内存和恢复时间不可控
- 定期做快照:将状态机当前状态保存为 Snapshot
- Snapshot 之前的日志可以丢弃
- 新节点加入时先传 Snapshot,再增量同步
ZAB(ZooKeeper)vs Raft 对比#
| 维度 | ZAB | Raft |
|---|---|---|
| 设计目标 | ZooKeeper 专用的原子广播 | 通用分布式共识 |
| Leader 选举 | 选最大 ZXID 的节点 | 随机超时 + 多数投票 |
| 任期标识 | epoch | term |
| 模式 | 恢复模式 / 广播模式 | 无显式模式,选举和复制无缝衔接 |
| 日志对齐 | 恢复阶段有 DIFF/TRUNC/SNAP | AppendEntries 逐步回退对齐 |
| 工程实现 | etcd 不用、ZooKeeper 用 | etcd / Consul / TiKV 都用 Raft |
本质相同: 都是 Leader-based、多数派确认、心跳检测。Raft 更强调可理解性。
Raft vs Paxos#
| 维度 | Paxos | Raft |
|---|---|---|
| 可理解性 | 极难(论文公认晦涩) | 易理解(论文以可理解性为目标) |
| Leader | Multi-Paxos 需要 Leader,Basic Paxos 不需要 | 强制 Leader |
| 工程实现 | 很少有完全按论文实现的 | 大量工程实现(etcd, Consul) |
面试回答(2分钟版)
Raft 把分布式共识拆成三个子问题。Leader 选举:每个节点有随机超时,超时后转为 Candidate,term+1 发起投票,多数票当选。随机超时避免 split vote。日志复制:Leader 收到写请求后追加日志,并行发 AppendEntries 给 Follower,多数确认后提交。Follower 日志不一致时通过回退 nextIndex 对齐。安全性:投票时检查 Candidate 日志是否足够新,保证已提交日志一定在新 Leader 中。脑裂防护靠 term 递增——网络分区时旧 Leader 无法提交(没有多数派),新 Leader term 更大,恢复后旧 Leader 自动降级。和 ZAB 本质相同(Leader-based + 多数派),Raft 更通用、更易理解,etcd 和 Consul 都用 Raft。
追问与易错
追问方向:
- “Raft 脑裂怎么解决?”→ term 递增 + 多数派,旧 Leader 无法提交,恢复后自动降级
- “日志复制和提交的区别?”→ 复制是写到 Follower 磁盘,提交是多数确认后应用到状态机
- “Raft 和 Paxos 优缺点?”→ Raft 易理解但强制 Leader(单点瓶颈),Paxos 理论更优但工程难实现
- “Leader 宕机后数据会丢吗?”→ 已提交的不会丢(多数节点有),未提交的可能丢
易错点:
- ❌ “两个 Leader 会脑裂”——term 大的有效,小的自动降级,不会同时有两个有效 Leader
- ❌ “Leader 写完就算提交”——需要多数节点确认
- ❌ “Raft 保证线性一致性读”——默认读 Leader 但可能读到旧数据(Leader 可能已过期),需要 ReadIndex 或 Lease Read 优化