Skip to content
Go back

Raft 为什么能保证同任期同索引的日志内容一定相同?

Edit page

这是 Raft 算法中最核心的安全性保证之一,被称为 日志匹配特性(Log Matching Property)

在 Raft 中,如果两个日志条目拥有相同的 任期号(Term)索引(Index),那么它们存储的 命令内容(Command) 一定完全相同,且该索引之前的所有日志也完全相同。

Raft 通过以下三个关键机制来保证这一性质:

1 选举安全性:一个任期只有一个 Leader

在任何一个任期内,Raft 保证最多只有一个节点成为 Leader。既然任期 T 只有一个 Leader,那么所有标记为 Term = T 的日志条目,源头都只能是这一个 Leader。不存在两个不同的 Leader 在同一个任期写了不同内容的情况。

2 Leader 的日志创建规则:只有 Leader 创建日志,且只追加

  • 只有 Leader 可以创建新的日志条目(Follower 和 Candidate 不能主动创建)
  • Leader 对自己的日志是 Append-only(只追加) 的。一旦 Leader 在索引 I 处创建了一个条目,它永远不会修改、覆盖或删除该索引处的条目

对于任期 T 的 Leader,它在索引 I 处只能写入 一次 内容。一旦写入,该内容就固定了。

3 日志复制与一致性检查

当 Leader 向 Follower 复制日志时,会携带 prevLogIndexprevLogTerm。Follower 会检查自己本地的上一条日志是否与 Leader 一致:

  • 如果不一致,Follower 会拒绝该请求,并回溯索引继续尝试,直到找到一致点
  • 一旦找到一致点,Leader 会强制 Follower 截断(Delete) 冲突点之后的所有日志,然后 追加(Append) Leader 的日志

这保证了 Follower 的日志是 Leader 日志的子集或最终会趋同于 Leader。


4 逻辑证明

我们可以通过简单的逻辑推导来理解为什么内容一定相同:

  1. 源头唯一:假设存在条目 $E_1 = (Term=T, Index=I, Content=A)$ 和 $E_2 = (Term=T, Index=I, Content=B)$
  2. 创建者唯一:因为 $Term=T$,根据选举安全性,这两个条目必然是由 同一个 Leader(节点 L) 创建的
  3. 行为唯一:节点 L 在任期 $T$ 期间,对于索引 $I$,只能执行一次”追加”操作。它不可能在索引 $I$ 先写入 $A$,然后再写入 $B$
  4. 结论:因此,$A$ 必须等于 $B$

5 常见的疑问:日志冲突怎么解决?

你可能会问:“如果 Follower 之前有一个旧 Leader 留下的冲突日志怎么办?”

场景:旧 Leader (Term 5) 在 Index 10 写了命令 A。新 Leader (Term 6) 在 Index 10 写了命令 B。

注意,这里 Term 不同(一个是 5,一个是 6)。这并不违反”同任期同索引内容相同”的规则。

当新 Leader (Term 6) 复制日志给 Follower 时,会发现 Follower 在 Index 10 的 Term 是 5,而自己是 6。根据 Raft 规则,Follower 会 删除 Index 10 及其之后的所有日志,然后接受新 Leader 的 Term 6 日志。

最终集群中 Index 10 的位置,要么全是 (Term 5, Content A),要么全是 (Term 6, Content B)。永远不会同时存在 (Term 5, Index 10, A) 和 (Term 5, Index 10, C)。

6 总结

Raft 保证”同任期同索引内容相同”的核心在于:日志条目的”身份证号”(Term + Index)是由唯一的 Leader 在唯一的时刻生成的,且 Leader 自身不可篡改历史。 这一特性是 Raft 实现状态机安全(State Machine Safety)的基础,确保了所有节点最终执行相同的命令序列。


Edit page