连载中 13/20

Raft 深入:两条提交红线与一份不可能丢的日志

2026-07-01 · 4058 阅读 · 0 评论 · 0 赞

入门解决「怎么活」,深入解决「怎么不丢」

上一篇的选举与复制解决的是集群「活着」的问题;这一篇抠的是「活得不丢、不乱」。Raft 论文用五条安全性性质收束这件事,对做业务的人,真正需要内化的是两条提交红线和一套脑裂免疫逻辑。先看最容易想错的那条——「过半 ACK 就提交」其实是有条件的。

红线一:只提交当前任期的日志

直觉版本是「日志复制到过半就 commit」,但直接这么实现会丢数据。用一个经典时间线(Raft 论文 Figure 8 的故事)还原:

t1  S1 当 Leader(Term=2),写日志 e2.2,复制到 S2,但还没过半
t2  S1 掉线;S5 凭「日志不比自己新」的例外当选(Term=3)
    —— S5 没有 e2.2,但它的日志更新(Term 更高),合法
t3  S5 写自己的日志 e3.2,又掉线
t4  S1 回来重新当选(Term=4),继续把 e2.2 复制到过半(S1,S2,S3)
    ← 按直觉此刻 e2.2 已「过半」,但它属于旧任期 Term=2,仍不许提交
t5  若此刻允许提交 e2.2,然后 S1 再掉线、S5 当选(Term=5)
    S5 会用 e3.2 覆盖 e2.2 —— 一条「已提交」的数据就这么丢了

Raft 的红线因此写成:commitIndex 只能在当前任期的日志上推进;旧任期日志必须「搭车」——等当前任期有新日志提交时,一并提交。t4 的 S1 想提交 e2.2,得先写一条 Term=4 的新日志让它过半,借新日志的提交把 e2.2 一起抬过线。这条红线略显迂回,却换来一个铁一般的不变量:凡是宣称已提交的数据,任何未来 Leader 都无法覆盖。

日志新旧的精确判定

选举纪律里「日志不比自己新就拒绝投票」,「新旧」的判定规则要一字不差:先比最后一条日志的 Term,Term 大者新;Term 相同再比日志索引,长(大)者新。两步比较杜绝了两个方向的漏洞:Term 大但日志短的不会被误判落后,索引长但 Term 旧的也冒充不了新——选举永远选出「见多识广」的那一位,数据落后者连提名资格都没有。

「不可能丢」的数学直觉

把红线一、投票纪律、log matching 三块拼起来,可以得到一个不需要形式化证明的直觉:已提交意味着过半节点留了底稿;任何新 Leader 的当选也要过半投票;两个「过半」必有交集——交集里的节点既见过那条日志,又有投票权。而它只会投给「日志不比自己新」的候选人,于是新 Leader 必然见过那条已提交日志,当选后按 log matching 保留它。多数派交集 + 选举纪律,把「已提交数据不丢」从概率变成了数学。这正是第 7 篇 Redis 丢锁与 ZAB 共识的分水岭所在。

读请求与脑裂

写要走 quorum,读呢?一个 Leader 读写状态可能落后(它刚被罢免自己还不知道),直接读会违反线性一致。etcd 提供两个档位:ReadIndex——Leader 先向多数派确认「我还是现任」再读,不写日志、一次往返;Lease Read——Leader 靠心跳租约自信自己还在任,零往返直读,快但依赖心跳时序。锁场景里「我还有锁吗」这类判断走 ReadIndex 档位,比 ZK 的 sync 命令语义更精确。至于脑裂:少数派侧的旧 Leader 继续发号施令,但过半 ACK 凑不齐,一条日志都提交不了;多数派侧选出新 Leader 正常服务——分裂只冻结旧的一侧,不毒化新的一侧,分区恢复后旧 Leader 看到 Term 落后自动退位(第 9 篇 epoch 退位的同款剧情)。

机制作用护栏对象
只提交当前任期防止旧日志被新 Leader 覆盖已提交数据不丢
日志新旧判定落后者没有被选举权Leader 数据完整性
log matching日志强制连续无分叉副本一致性
ReadIndex / Lease读不违反线性一致读的正确性
多数派交集脑裂只冻结一侧分区安全

协议课到此结业。下一篇回到应用层,看 etcd 怎么用 Raft 的两个产物——租约(Lease)和全局 revision——把分布式锁做得比 ZK 更简洁:一个 lease 管生死,一个 revision 排公平。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Raft#提交规则#ReadIndex#Lease Read#脑裂#日志安全

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞