目录标题
Raft 日志复制是 Raft 共识算法的核心机制之一,用于在多个副本(节点)之间保持一致的日志状态。下面我们将从 Raft 的整体结构、日志复制机制、流程细节、异常处理与优化手段几个方面,深入剖析这一过程。
🧠 一、Raft 结构概览
Raft 将一个分布式系统抽象成多个参与共识的节点,每个节点可处于三种角色:
- Leader(领导者):负责处理客户端请求、日志复制、心跳。
- Follower(跟随者):被动接受来自 Leader 的指令。
- Candidate(候选者):用于发起选举,争取成为 Leader。
在日志复制过程中,只有 Leader 负责接收客户端请求并发起复制操作。
📦 二、Raft 日志复制的基本目标
让所有节点维护一份完全相同、有序的日志副本,每一条日志代表一条客户端指令(如写入、删除等)。
特点:
- 日志是有序编号的列表,每条记录有唯一编号
(term, index)
- 一致性保证:一旦多数节点写入日志并 commit,该日志就不可更改
- 顺序执行:日志在所有节点应用顺序必须一致
📜 三、Raft 日志复制流程(正常情况下)
假设:
客户端发送写入请求,Raft 集群有 5 个节点,节点 1 是 Leader。
步骤详解:
步骤 | 操作内容 | 说明 |
---|---|---|
1 | 客户端发送命令给 Leader | 例如:PUT key=foo, val=bar |
2 | Leader 将命令封装为日志条目 | 日志项:(term=5, index=42, data=PUT foo=bar) |
3 | Leader 将日志条目加入本地日志 | 本地立即保存(可写入 WAL) |
4 | Leader 发送 AppendEntries RPC 给所有 Follower | 包含:当前日志条目、前一条日志的索引和 term、Leader 的 commit index |
5 | Follower 检查是否匹配前一日志 | 如果前一日志 (prevLogIndex, prevLogTerm) 不匹配,拒绝 |
6 | 若匹配,Follower 追加该日志 | 并返回成功响应 |
7 | Leader 收到多数成功响应 | 即超过半数(多数派)已写入该日志 |
8 | Leader 提交该日志条目(commit) | 并将最新 commitIndex 发送给所有 Follower |
9 | Follower 也提交该日志 | 应用日志条目到状态机,例如写入 RocksDB |
10 | Leader 给客户端返回成功 | 至此,写入完成且一致性保障完成 |
📌 四、关键字段说明
字段名 | 说明 |
---|---|
term | 当前任期,用于标识 Leader 的合法性 |
log[] | 日志数组,每个条目含 (term, index, command) |
commitIndex | 当前已提交的最大日志索引 |
lastApplied | 当前已应用到状态机的最大索引 |
nextIndex[i] | Leader 认为 Follower[i] 应该接收的下一条日志索引 |
matchIndex[i] | Leader 认为 Follower[i] 已复制的最大日志索引 |
⚠️ 五、异常处理与恢复机制
1. 日志不匹配处理(Follower 拒绝追加)
- 若 Follower 检测到
(prevLogIndex, prevLogTerm)
不匹配,说明日志与 Leader 有分歧。 - Follower 拒绝该 AppendEntries。
- Leader 减小
nextIndex[follower]
,尝试向 Follower 发送更早的日志,回退式同步。
2. 快照(Snapshot)
- 当 Follower 落后太多时,Leader 不再用日志同步,而是发送快照,直接覆盖整个状态。
3. 日志压缩(Log Compaction)
- 已提交且已应用的日志可以被压缩、丢弃,防止日志无止境增长。
🧩 六、Leader Commit 的严格条件
Leader 不能随意 commit 日志,必须满足以下条件:
- 日志已复制给多数派;
- 该日志是当前 term 的日志(term == currentTerm)。
这一规则防止 旧 Leader 日志被错误提交,维护系统一致性。
🛠 七、优化技术
技术 | 说明 |
---|---|
AppendEntries 批量传输 | 一次 RPC 携带多条日志,减少网络开销 |
Pipeline AppendEntries | 异步发送,提升并发性能 |
快照替代日志重放 | 避免旧日志过多时的同步瓶颈 |
延迟应用日志 | 写入日志后先返回,状态机异步应用(如 TiKV 的 apply pool) |
WAL 与日志解耦 | Raft 日志和底层存储的 Write-Ahead Log 可分离并优化 |
📈 八、在 TiKV 中的实际应用
- 每个 Region 是一个 Raft group。
- 每个写请求需先写入 Raft 日志,commit 后再写入 RocksDB(LSM Tree)。
- Leader 管理
nextIndex
与matchIndex
,精准控制副本同步。 - 使用异步 apply 线程池避免主线程阻塞。
- 支持 snapshot、Region 合并拆分、日志 GC。
✅ 九、总结一句话:
Raft 日志复制的目标是让所有副本维护一致、有序的日志副本。它通过 AppendEntries、任期与索引校验、日志回退与快照恢复等机制,在保证一致性的同时提供高性能、高可靠的副本同步能力。
如果你想了解:
- Leader 选举机制
- 快照发送的详细过程
- 如何避免脑裂
- Raft 与 Paxos 的对比