graph TD B[领导者收到请求] --> D["追加日志条目到本地日<br>并行发送AppendEntries RPC"] D --> E{大多数节点确认?} E -->|是| F[提交条目并应用状态机] E -->|否| G[等待重试或超时] F --> H[通知跟随者提交日志] H --> I[返回响应给客户端]
日志:分布式系统的记忆载体
在分布式系统中,日志(Log)是系统的核心记忆单元。Raft通过以下设计确保日志强一致性:
- 唯一连续性:每个日志条目有全局唯一的递增索引(Index)和任期号(Term)
- 操作原子性:所有节点最终存储相同顺序的操作序列
- 状态确定性:日志内容决定状态机的最终状态
日志结构解剖
classDiagram class LogEntry { +Index: 连续递增编号 +Term: 创建时的任期号 +Command: 状态机指令 }
日志复制五部曲
1. 客户端请求接收
领导者将客户端请求包装为日志条目,赋予当前任期号和最新索引+1
2. 并行广播(AppendEntries RPC)
sequenceDiagram participant Leader participant Follower1 participant Follower2 Leader->>Follower1: 发送prevLogIndex=4, prevLogTerm=2 Leader->>Follower2: 发送prevLogIndex=4, prevLogTerm=2 Follower1-->>Leader: 一致性检查成功 Follower2-->>Leader: 一致性检查失败
参数包含:
- prevLogIndex:前一个日志的索引
- prevLogTerm:前一个日志的任期号
- entries[]:新日志条目
- leaderCommit:已提交的最高索引
3. 跟随者一致性验证
跟随者执行关键检查:
if self.log[prevLogIndex].term != prevLogTerm:
return False # 日志不一致
else:
append_new_entries() # 接受新日志4. 日志提交(Commit)
当日志复制到大多数节点时:
graph LR L[Leader] -->|提交索引5| A[Node A] L -->|提交索引5| B[Node B] L -->|提交索引5| C[Node C] A -->|应用状态机| SA[State] B -->|应用状态机| SB[State] C -->|应用状态机| SC[State]
5. 响应客户端
领导者应用日志到状态机后,将执行结果返回客户端
关键保障机制
日志匹配特性(Log Matching Property)
数学归纳法证明:
- 初始状态:空日志天然满足一致性
- 归纳步骤:若日志n的term相同,则n-1的term必然相同
- 传递结果:所有日志最终达成一致
冲突解决方案
当跟随者日志不一致时:
graph TD A[发现日志冲突] --> B[递减nextIndex] B --> C{匹配prevLogTerm?} C -->|是| D[删除冲突日志] C -->|否| B D --> E[追加新日志]
安全性屏障
- 选举限制:只有包含所有已提交日志的节点才能成为领导者
- 提交规则:只能通过当前任期日志间接提交旧日志
工程实践启示
- 批量提交:合并多个请求提升吞吐量
- 流水线优化:连续发送RPC无需等待响应
- 心跳复用:携带日志条目提高传播效率
Raft通过这种严谨的日志设计,将复杂的共识问题转化为可验证的日志操作序列,为分布式系统提供了可靠的一致性保障基础。