一致性笔记:日志复制:matchIndex 与提交规则
发布于:
这篇把 Raft 实现里两个最常见、也最容易误用的 index 讲清楚:
matchIndex(每个 follower 一份):leader 认为该 follower 已经复制到的最高日志 indexcommitIndex(leader 一份):系统已经可以对外承诺、并可应用到状态机的最高日志 index
核心结论:
matchIndex反映复制进度commitIndex反映提交承诺- 从 matchIndex 推 commitIndex 的规则,决定了“不会回滚已确认写”
1. 三个 index 的最小模型:nextIndex / matchIndex / commitIndex
leader 通常会为每个 follower 维护:
nextIndex[f]:下一次给 follower 发送的起始 indexmatchIndex[f]:已知 follower 已经匹配到的最高 index
leader 自己还有:
commitIndex:已提交的最高 index
flowchart TD
L[Leader state] --> N[nextIndex per follower]
L --> M[matchIndex per follower]
L --> C[commitIndex]
N --> R1[controls retry position]
M --> R2[reflects replication progress]
C --> R3[controls apply and client ack]
2. matchIndex 是怎么来的:AppendEntries 成功后推进
当 leader 给 follower 发 AppendEntries:
- follower 接受并写入日志
- 返回成功
leader 就可以更新:
matchIndex[f] = lastReplicatedIndexnextIndex[f] = matchIndex[f] + 1
sequenceDiagram
participant L as Leader
participant F as Follower
L->>F: AppendEntries with prevIndex and prevTerm
F-->>L: Success
L->>L: update matchIndex to lastIndex
L->>L: update nextIndex to lastIndex plus 1
失败时(prevIndex/prevTerm 不匹配),leader 通常会回退 nextIndex 并重试,直到对齐日志前缀。
3. commitIndex 怎么算:多数派的“共同进度”
在多数派提交下,leader 想找到一个 index i,满足:
- 至少有多数派副本的
matchIndex >= i
最直觉的计算方法:
- 把所有副本(含 leader 自己)的 matchIndex 收集起来
- 排序
- 取多数派位置的那个值
flowchart TD
A[Collect matchIndex] --> B[Sort]
B --> C[Pick quorum position]
C --> D[Candidate commit index]
然后 leader 会尝试把 commitIndex 推到这个候选值,但还需要一个非常关键的安全约束(下一节)。
4. 关键提交规则:不要“凭多数派”提交旧 term 的日志
这是 Raft 实现里非常关键、也经常被忽略的一点:
- leader 推进 commitIndex 时,通常只使用“当前 term 的 entry”作为锚点
原因:
- 旧 term 的 entry 可能在一些 failover 组合下出现“多数派复制看似满足,但并不能保证 leader completeness”
- 用当前 term entry 作为锚点,能保证一旦它被多数派复制,leader 的日志前缀具备更强的安全性质
工程上可以看到类似逻辑:
- 找到某个
i,它满足多数派 matchIndex - 但只有当
log[i].term == currentTerm时,才推进 commitIndex 到 i
flowchart TD
I[candidate i] --> Q[replicated to quorum]
Q --> T{"log term equals currentTerm"}
T -->|yes| C[advance commitIndex]
T -->|no| H[wait for newer entry]
5. 线上现象:为什么“慢副本”会把提交点拖死
可以看到两类典型指标:
matchIndex分布拉开:某些 follower 长期落后commitIndex推进变慢:写延迟变长
原因很直接:
- 多数派提交把提交点绑定在“第 W 个 ack”的路径上
- 如果慢副本经常落入多数派集合,就会放大尾延迟
6. 应当观测什么(从最有用的开始)
- per-follower
matchIndex与nextIndex(谁在落后) - follower backlog(欠账多少 entry / bytes)
- AppendEntries 的 RTT 分布(P95/P99)
- leader commit latency(从 append 到 apply 的端到端延迟,如果可打点)
- leader 变更频率(频繁选主会进一步拖慢复制)
7. 排障顺序
- 找出落后 follower(matchIndex 最低且持续)
- 看落后原因:网络/磁盘/CPU/快照安装
- 看拓扑:是否跨域导致 RTT 尾部过大
- 再调整参数:超时、批量、窗口、限速
8. 小结
- matchIndex 是“每个副本跟到哪”
- commitIndex 是“系统能承诺到哪”
- 从 matchIndex 推 commitIndex 的规则(尤其“当前 term 锚点”)是安全性的关键