SICI · 元婴五层 · 第二十一章

分布之壁

分布式系统原理 · 元婴五层·分布式道

你以为分布式系统就是多接几台机器。多出来的每一台机器,都是一道新的裂缝——光速有限、链路会断、节点会撒谎。你唯一的选择是:分区时,要一致性,还是要可用性?

分区之壁低语:「这五样东西,哪一样其实不需要分布式系统?赌一局。账,我记下了。」
序章

雾锁天机门

分布式之赌 · 反直觉下注 + 热区探索点亮

你推开天机门,门楣上刻着四个字:"多节点,共状态。"门内没有整齐的机房,只有一片翻涌的雾——雾里隐约可见无数光点在闪烁,像很多台机器在同时运转,却彼此看不见对方的屏幕。雾凝成一面横亘天际的半透明裂壁,壁面布满细密裂纹。

雾锁天机门:翻涌雾海中无数节点光点各自闪烁,一面布满裂纹的半透明分区之壁横亘天际
诺依曼老祖 诺依曼老祖

「分布式系统 = 多节点 + 消息传递 + 独立失败 + 共享状态。你以为多接几台机器就行——先过了它这关。」

分区之壁 分区之壁

「来得好。你既信'多接几台机器就行',与我赌一局——这五样东西,哪一样其实不需要分布式系统?」

分布式无例外预测挑战器

反直觉下注(五选一)→ 每件剪影打脸揭真相 → 五热区全亮拼合文明图谱

分区之壁逼你下注:这五样东西,哪一样其实不需要分布式系统?
详解 · K21-01 分布式系统的基本特征与挑战

分布式系统 = 多节点 + 消息传递 + 独立失败 + 共享状态——台上老祖给出的这条公式,正对应 Kleppmann《Designing Data-Intensive Applications》第 1 章的判断:你从"单机顺序执行"进入的,是一个"部分失败是常态"的世界。它与单机多线程的本质区别有三:其一,没有共享内存,一切协作都要经过网络,而网络只是"尽力而为"(best-effort)——消息会延迟、丢失、乱序、重复;其二,每个节点有自己的时钟,"同时"没有物理定义(S7 将正面撞上它);其三,故障是部分的:五台机器坏一台,剩下四台仍在跑,你必须精确知道"谁坏了、坏到什么程度"。Peter Deutsch 的"分布式计算八大谬误"第一条就是"网络是可靠的"——S0 台上那五件剪影(搜索引擎、银行核心、CDN、即时通讯、大模型训练)没有一件是例外:它们需要分布式,不是因为"多接几台机器更强",而是因为单机在规模与故障面前根本不成立。衡量这套代价的两个词是可用性(服务在线时间的比例)与可扩展性(加机器能否近似线性地加容量)——本章后续每一个协议,都是在拿一致性与这两个词做交换。

第一幕 · 自信→裂缝

灵阵台 · 部分失败之渊

五节点灵阵 + 心跳检测 + 故障注入 + 断链之伤形成

石径尽头是一座悬空石台,台上漂浮着五枚光球——五台计算节点,由灵链两两相连。诺依曼老祖站在台边。

诺依曼老祖 诺依曼老祖

「一台机器坏了,你知道——它不亮了。五台里坏了一台呢?你看见的是四台在跑、一台在说谎。你怎么知道是哪一台?」

分区之壁 分区之壁

「多接几台机器嘛,这有什么难的。去吧。」

灵阵台 · 部分失败实验

搭建灵阵 → 注入静默故障 → 心跳阈值调节(误杀/漏检)→ 断链触发分区

详解 · K21-13 容错机制:心跳检测 / 故障检测 / 故障恢复

台上那台心跳仪的滑块(1–40 秒)演示的是故障检测的根本权衡:阈值太短,一次网络抖动就把健康节点"误杀"(false positive,对应滑块 ≤2s 的警告);阈值太长,真死的节点要很久才被发现(false negative,漏检)。故障检测器只有两个可调参数——探测频率与超时阈值,两者共同决定检测速度与误判率,而且不存在完美检测器:在异步系统里,你无法区分"对方很慢"与"对方已死";Chandra-Toueg 1996 年证明,只有借助"最终完美"检测器,共识问题才有解。工程上的进阶是 Phi Accrual 故障检测器(Hayashibara 等 2004):不输出"死/活"的二元判决,而是输出连续的怀疑度 φ,Cassandra、Hazelcast 都在用它。检测到故障之后的恢复同样有讲究:崩溃-恢复模型下节点要靠预写日志(WAL)重放状态;为防"误杀后新旧双主并存",还要给领导者发租约(lease)——租约到期自动失效,比一次心跳的承诺更硬。最后把故障换算成可用性:Availability = MTBF/(MTBF+MTTR),分子是平均无故障时间,分母加上平均修复时间——同一台机器,把修复从 1 小时压到 5 分钟,往往比把故障率降低十倍更划算。年停机预算换算:99.9% ≈ 8.8 小时,99.99% ≈ 52.6 分钟,99.999% ≈ 5.3 分钟——每多一个 9,都是"冗余 + 检测 + 恢复"三件套一起买来的。

第一幕 · 试错

复制殿 · 数据复制之幻

主从/多主/无主复制 · Quorum W+R>N · 冲突解决

灵阵台后是一座环形大殿,殿中央悬浮着一块发光的数据晶——你的本命数据。殿壁上刻着三种阵法图:主从阵、多主阵、无主阵。

诺依曼老祖 诺依曼老祖

「只存一份,坏了就没了。存多份呢?——多存一份,就多一份不一致的可能。」

分区之壁 分区之壁

「两个主节点同时写同一行,你觉得谁说了算?你连'谁先谁后'都答不上——因为你还没有全局时钟。」

复制策略三选一 · 后果推演

主从(单点+异步丢失)→ 多主(冲突爆发)→ 无主(W/R Quorum 调参)

主从阵
主写从复制,写入快
多主阵
多主并写,可用性高
无主阵
W/R Quorum,灵活
详解 · K21-07 数据复制策略:主从 / 多主 / 无主复制

复制是拿"多一份副本"换"多一份保命",但每一份副本都是一份新的不一致机会——台上三张阵法卡的后果推演,对应 Kleppmann 第 5 章的三种拓扑。主从(单领导者):所有写走主节点,再同步给从节点,简单且无写冲突;代价是故障切换窗口(从节点提升为主需要数秒到数十秒)与异步复制的丢失窗口——主节点确认写入之后、从节点收到之前宕机,这条写就永远丢了。多主:多个主同时接受写,可用性最高,但两个主同时改同一行必然冲突(正是台上"你连谁先谁后都答不上"的那一幕),解决手段要么退化为"最后写入胜出"(LWW,依赖时钟,可能丢更新),要么用 CRDT(可交换合并的数据结构,Riak 的做法)。无主(Dynamo / Cassandra 风格):写发 W 个副本、读发 R 个副本、共 N 个副本——当 W+R>N 时,读集与写集必有交集,交集至少 W+R−N 个副本,所以读必能撞见最新值:这正是台上 N=3、W=R=2 的强一致档位(2+2>3);把 W 或 R 调到 1,可用性上去了,旧数据就会漏出来(1+2≤3)。补上两件工程配套:读修复(读到旧副本顺手写回)与反熵(后台周期性对齐副本)——它们让"最终一致"真的会"最终"。

第一幕 · 第一崩塌

分区之壁初现 · CAP 定理

CP/AP/CA 三选一 · 不可能证明骨架 · BASE 妥协

复制殿地面忽然裂开——一面半透明的巨壁从裂缝中升起,把你的五节点灵阵劈成两半:左侧两台,右侧三台。诺依曼老祖的声音从壁的另一侧传来,模糊不清。

分区之壁 分区之壁

「这就是我。网络一定会分区——光缆会被挖断,交换机会丢包,机房会断电。你唯一的选择是:分区时,你要一致性,还是要可用性?」

诺依曼老祖 诺依曼老祖

「这是第一定律。不是你设计得不够好,是物理不允许。」

CAP 不可能相图探索器

CAP三角相图 → 三选一抉择推演 → CA触发不可能证明 → BASE浮现

选 CP
一致性优先
选 AP
可用性优先
选 CA
试图全都要
详解 · K21-02 CAP定理:一致性/可用性/分区容错性的三选二

台上那七步推演,正是 Gilbert & Lynch 2002 年对 Eric Brewer 2000 年 PODC 猜想的形式化证明骨架(Brewer 本人在 2012 年 IEEE Computer 上补过《CAP 十二年后》的回顾)。三个字母先要校准:C 指线性一致性(不是 ACID 的那个 C);A 指"每个非故障节点都能在合理时间内返回有意义的响应"(不是 HTTP 200);P 指"网络允许任意丢消息"——它不是可以勾选的选项,而是物理默认值。证明的关键一步就是台上那一幕:分区把节点劈成 G1、G2 两组,客户端向 G1 写 x=1、向 G2 写 x=0;可用性要求两边都接受写入,一致性要求两边读出同一个值,而两组之间没有消息可传——矛盾。所以严格的表述不是"三选二",而是"分区发生时,C 与 A 只能保一个";Kleppmann 第 9 章还特意强调:C 和 A 都是连续谱,不是开关。PACELC(Abadi 2012)补上另一半真相:分区时在 PC 与 PA 之间选,无分区时还要在延迟(L)与一致性(C)之间再权衡一次。选 CP 的代表是 ZooKeeper、HBase;选 AP 的代表是 Cassandra、Dynamo——台上三选一卡片的每一次点击,都是在真实系统之间站队。

详解 · K21-03 BASE理论与最终一致性

BASE——Basically Available(基本可用)、Soft state(软状态)、Eventually consistent(最终一致)——是 eBay 工程师 Dan Pritchett 2008 年在 ACM Queue 上为 AP 系统立的"宪法",名字本身就是对 ACID 的戏仿。它的承诺是:分区期间允许数据不一致,但分区恢复后、停止写入再等足够久,所有副本会收敛到同一个值。"最终"没有时间上界,这是它最常被诟病之处——Kleppmann 第 9 章提醒:最终收敛的系统也可能先收敛到一个"错"的值(比如 LWW 依赖坏时钟丢掉的更新)。让"最终"真正发生的两件配套工具,你在 S2 已经见过:读修复(读到旧副本顺手写回)与反熵(后台周期性对齐副本)。收敛的判据还有层次:只保证"写入停止后副本趋同"是收敛一致性;若还保证副本以相同顺序应用相同的写集合,才是严格意义的最终一致性。工程上的 BASE 典型是 Dynamo 风格购物车:分区时两边都能加购(保 A),合并时用 CRDT 取两个购物车的并集——正是 S2 多主阵法卡上"冲突可合并"的那条活路。台上 BASE 浮现的那一刻,墙没有变矮——你只是学会了在墙下生活。

第二幕 · 短暂掌控

共识祭坛 · Paxos 协议

Prepare→Promise→Accept 三阶段 · 多数派 Quorum · 分区测试

壁后是一座环形祭坛,坛上漂浮着三枚"提案珠"和五枚"承诺珠"。诺依曼老祖的声音从远处传来。

诺依曼老祖 诺依曼老祖

「CAP 说你不能同时要 C 和 A。但如果多数节点同意同一个值——即使少数派被分区隔开,多数派仍然一致。这叫共识。有个协议叫 Paxos,Lamport 1989 年提出、1998 年才正式发表——从提出到被读懂,花了一代人。它被证明是正确的。但没有人能读懂它。」

分区之壁 分区之壁

「正确但读不懂——这本身就是一种失败。」

Paxos 三阶段角色扮演

Proposer 提案 → Acceptor 承诺 → Learner 学习 → 分区注入看少数派

详解 · K21-04 Paxos一致性协议:Basic Paxos原理

祭坛上那三步——①Prepare(N)→②Promise→③Accept(N,V)——是 Basic Paxos 对"单个值达成共识"的完整循环(Lamport 1989 年构思、1998 年《The Part-Time Parliament》正式发表,传说中的"最难懂论文")。角色三分:Proposer 提案、Acceptor 投票、Learner 学习结果。安全性的两根支柱:Acceptor 一旦对编号 N 做出承诺,就不再接受编号更小的 Prepare(提案编号单调前进);Promise 回流时若 Acceptor 已接受过值,必须捎带"我已接受的最高编号值",Proposer 必须改提这个值(已决议的值永不被推翻)。多数派是关键算术:N=5 个 Acceptor,任意两个多数派各 ≥3 台,交集至少 3+3−5=1 台——正是这台交集机器保证两个互相竞争的提案不可能各自通过。台上 Prepare(N=1) 拿到 3/5 承诺即可推进到 Accept;分区注入后 Acc4/5 被隔开,2/5 凑不成多数、无法 Commit——共识系统在分区时天然牺牲少数派的可用性,这就是"共识本身就是 CP 选择"的算术来源。两个著名补丁:多 Proposer 互相抬价导致的活锁,用"选一个 Leader 负责提案"解决,即 Multi-Paxos;工程实现从 Google Chubby 到 ZooKeeper 的 ZAB、etcd 的 Raft,全都长在这副骨架上。

第二幕 · 第二崩塌

Raft 之环 · 共识流水线

Leader 选举 + 日志复制 + 分区注入脑裂 + Paxos/Raft 对比

祭坛化作一个圆环,五枚节点围环而坐。环上漂浮着一份协议卷轴——Raft。

诺依曼老祖 诺依曼老祖

「Raft 把共识拆成三件事:选 leader、复制日志、保安全。它比 Paxos 好懂——但分区时,它一样会让你痛。」

分区之壁 分区之壁

「上次你用 Paxos 绕我,这次用 Raft?好。我等着。」

Raft 共识流水线动画器

随机超时选 Leader → 日志复制流水线 → 分区注入脑裂 → 回滚 → Paxos/Raft 对比

详解 · K21-05 Raft一致性协议:领导者选举/日志复制/安全性

Raft(Ongaro & Ousterhout,USENIX ATC 2014,论文副标题就是"In Search of an Understandable Consensus Algorithm")把共识拆成台上那三件事:领导者选举、日志复制、安全性。选举:每个 Follower 持有一个随机化的选举超时(论文典型值 150–300ms,随机化正是为了错峰——避免大家同时超时、选票分裂、无人过半),超时未见心跳就自增任期 term、变身 Candidate、向全网拉票;N=5 的集群拿到多数票(≥3)即当选。日志复制:所有写经 Leader,Leader 将日志条目并行发给 Follower,收到多数派确认(台上计数器走到 Quorum 3/5)即 Commit 并随下次心跳广播。安全性铁律中最关键的是"选举限制":投票者只投给日志至少与自己一样新(term 更高、或同 term 但索引更长)的 Candidate——这保证已 Commit 的日志永远不会被新 Leader 丢失。台上脑裂一幕的算术:旧 Leader 困在少数派(2 节点),永远凑不齐 3/5 确认,写只能悬着不 Commit——不是"提交了再回滚",Raft 根本不让它提交;多数派(3 节点)选出新 Leader,分区恢复后旧 Leader 看到更高 term 自动退位,未 Commit 的日志被新 Leader 覆盖消散。etcd、Consul、TiKV、Kafka KRaft 都在跑 Raft——它的胜利不在更强,而在第一次有人把共识写到本科生能复现的程度。

详解 · K21-06 Paxos vs Raft的可理解性与工程实践对比

对比面板上的四行差异,每一行都对应真实的工程决策。其一,Leader 强度:Paxos 允许多个 Proposer 同时提案(理论更对等,代价是活锁风险与实现复杂度);Raft 强 Leader——所有请求必须经 Leader,线性一致读也能走 Leader(配合 ReadIndex 或租约,省掉一轮写日志)。其二,可理解性:Ongaro 把"问题分解"当作设计目标本身,论文附带用户研究(43 名学生里 Raft 的正确率显著更高)与 TLA+ 形式化验证;Paxos 这边,Lamport 2001 年专门写《Paxos Made Simple》补救,开篇承认用平实的英语讲其实很简单——原文的失败一半要算在希腊岛屿寓言的包装上。其三,日志结构:Multi-Paxos 允许日志有空洞(每个槽位独立共识),Raft 要求日志连续复制、AppendOnly,状态机重放的实现更直接。其四,生态:Multi-Paxos 系(Chubby、Spanner 的 Paxos 组)多为大厂闭源实现,Raft 系(etcd、Consul、TiKV、Kafka KRaft)几乎全部开源,客观上让 Raft 成了新系统的默认选择。但面板最后一行才是本章主题:两者都是多数派 Quorum、都是 CP 选择、分区时少数派都沉默——换协议换不掉 CAP,换得掉的是工程师的睡眠质量。

第二幕 · 断链复发

事务天平 · 2PC/3PC 与一致性级别

2PC 阻塞(断链复发)+ 3PC 补救(断链愈合)+ 一致性级别权衡

天平殿里悬着一架巨型天平,左盘刻"Prepare",右盘刻"Commit"。天平下方是一条断裂的灵链——与 S1 灵阵台上断掉的那条一模一样。

诺依曼老祖 诺依曼老祖

「跨节点事务用 2PC:先 Prepare,再 Commit。但如果分区发生在 Prepare 和 Commit 之间——协调者联系不上参与者,参与者不知道该提交还是回滚。它们会永远等下去。」

分区之壁 分区之壁

「你 S1 断的那条链——还记得吗?它回来了。我帮你存着呢。现在它来收利息了。」

2PC/3PC 事务天平

2PC 执行(断链复发阻塞)→ 3PC 补救(断链愈合)→ 一致性级别滑块

详解 · K21-09 分布式事务:两阶段提交(2PC)/三阶段提交(3PC)

天平上那只持锁僵死的 P3,正是 2PC(Gray 1978 年给出雏形)最著名的病灶。两阶段:①Prepare——协调者问所有参与者"能提交吗",参与者投 Yes 之前必须把所需资源锁住并写入持久日志;②Commit——全票 Yes 才广播 Commit,任一 No 则全体 Abort。阻塞的精确时序:P3 已投 Yes 并持锁,此后协调者与它分区(台上"S1 断链复发"打在协调者↔P3 那条链上)——它既不敢提交(别的参与者可能在 Aborting)也不敢回滚(别的参与者可能已 Commit),只能持锁无限等待,把锁的毒气扩散给所有竞争该数据的请求。3PC(Skeen 1981)在中间加一个 PreCommit 阶段:参与者先收到"准备提交"的广播,协调者失联时能凭阶段状态与超时规则自行了断——阻塞从"必然"降为"少数场景",代价是同步轮次多一倍,且同步网络的假设被分区打破时仍可能不一致(台上文案的定性:"把永久阻塞变成可能不一致")。工程界的真实选择:单库内部用 2PC(数据库的老牌实现);跨服务几乎全改走 Saga/TCC 补偿模式——不追求原子提交,改用"可回滚的业务序列",把一致性问题换成了编排问题;Percolator 与 XA 协议则是 2PC 在 Google 与数据库互操作层的两个著名化身。

详解 · K21-08 分布式一致性:线性一致性/顺序一致性/因果一致性

滑块上那四档,是"一致性不是开关,是阶梯"的直接证据(Kleppmann 第 5、9 章;形式化原典是 Adya 1999 年的博士论文)。线性一致(Linearizability):每个操作看起来在调用与返回之间的某个瞬间"瞬时完成",读必见最新写——最强,代价是任何一次读都要走多数派(或 Leader + ReadIndex/租约),延迟与分区可用性最差。顺序一致(Sequential Consistency):所有节点看到同一个全局顺序,但这个顺序不必匹配真实时间——两台机器可以对"谁先谁后"达成一致,哪怕与墙上时钟相反。因果一致(Causal Consistency):只保证有 happens-before 关系的操作保序,无因果的操作可乱序——S7 的向量时钟正是判定因果的工具,COPS 与 MongoDB 的 causal session 都落在这档。读己写(Read-your-writes):你自己的写对自己立即可见——最弱的一档,却对用户体验最要命:发帖后刷新看不到自己的帖子,用户立刻投诉。降档的收益直接而具体:读从"走多数派"降到"读本地副本",P99 延迟可以砍掉一个跨机房 RTT;代价是读到旧数据的窗口拉长。滑块每往右一档,就是在用一致性换延迟与可用性——这是 CAP 之内也成立的第二条权衡轴。

第二幕 · 认知重构

时钟殿 · 没有全局时间

Lamport 逻辑时钟 + 向量时钟 + happens-before + TrueTime 彩蛋

时钟殿里没有钟。只有一面墙上挂满了碎裂的时钟——每个节点的时钟都显示不同的时间。

诺依曼老祖 诺依曼老祖

「单机有一个时钟,它说了算。五台机器有五个时钟——你说谁说了算?NTP 同步?同步有误差。GPS 授时?机房没有窗户。在分布式系统里,'同时'这个词没有意义。你只能定义'谁导致了谁'——这叫 happens-before。」

事件日志排序 · Lamport / 向量时钟

物理时钟排反 → Lamport(无法区分并发)→ 向量时钟(精确检测并发)→ 共识=模拟逻辑全局时钟

详解 · K21-14 分布式时钟:逻辑时钟/Lamport时间戳/向量时钟

墙上那六个事件 A–F,就是 Lamport 1978 年《Time, Clocks, and the Ordering of Events in a Distributed System》的开场实验——这篇论文后来拿了分布式计算领域的首届 PODC 最具影响力论文奖(今 Dijkstra 奖)。物理时钟为什么不行:NTP 同步有毫秒级误差、晶振漂移累积可观,台上 N3 的时钟"倒走"(10.2→9.9)并不夸张。Lamport 时钟的规则只有两条:本地事件 L=L+1;收到消息 L=max(L, L_msg)+1。它构造的是 happens-before 偏序的扩展全序:若 a→b 则 L(a)<L(b)——但逆命题不成立:L(A)=1、L(C)=3 并不代表 A"早于"C,Lamport 时钟无法区分"先后"与"并发",这正是台上那一行的短板。向量时钟(Fidge 1988 / Mattern 1989 独立提出)把标量升级为向量:V[i] 记录"我知道节点 i 已发生多少事件",规则同样是本地分量自增 + 收消息逐分量取 max;判定法则:V(a) 与 V(b) 若一个逐分量 ≤ 另一个则存在偏序,互有大小则并发(concurrent)——[1,0,0] 与 [0,1,0] 一眼即见,S6 的因果一致性级别靠它落地。代价是 O(N) 的存储与消息开销。彩蛋的 Spanner TrueTime 走了另一条极端:GPS + 原子钟把不确定度压到 ±7ms 内,提交前先等不确定度窗口过去,用"原子钟买钱"硬造出外部一致性——诺依曼老祖的批注精准:这是工程奇迹,不是物理定律。

第三幕 · 顿悟→突破

分片与计算 · 接受现实

数据分片 + 一致性哈希 K/N + MapReduce 容错重跑

时钟殿后是一片开阔的星图台,星图上密布着无数数据光点——你的整个数据集。

诺依曼老祖 诺依曼老祖

「你打不过分区。但你可以让分区只影响一小片数据。把数据拆开——一台节点只存一部分。分区了?坏运气只落在那部分数据上。节点不够用了?加一台。但加一台意味着数据要重新分配——怎么搬最少?」

一致性哈希 K/N 迁移公式组装器

分片三选一 → mod N 全搬迁 → 公式组装 K/N → 哈希环增删 → MapReduce 重跑

范围分片
哈希 mod N
目录分片
详解 · K21-10 分布式存储系统:数据分区/分片策略

三张分片卡对应 Kleppmann 第 6 章的三大策略。范围分片:按 key 的区间切(A–M 一台、N–Z 一台),范围查询高效、对 B+ 树友好(Bigtable/HBase 的 rowkey 都是它),代价是倾斜——所有以 A 开头的新用户都砸向同一台,热点集中。哈希分片:key 哈希后取模,分布均匀、写热点消失,代价是范围查询报废(相邻 key 被打散)——正是台上 mod 5 那张卡。目录分片:维护一张全局查找目录,最灵活(可任意迁移、可精细配额),代价是目录自己成为新的单点与瓶颈,还多一跳转发。分片与复制必须叠加:每片通常再配多副本(Kafka 的 partition + replication factor、MongoDB 的 shard + replica set),因为分片解决"装不下"、复制解决"坏不起"——两者正交,缺一不可。热点的动态解法:监控到某片过载就让它再分裂(HBase 的 region split、Cassandra 的自动均衡);key 设计上则要避开"末尾递增"这类自毁式热点写法。台上 mod N 卡的全搬迁警告只是第一层:分片方案一旦上线,迁移成本就主导了整个扩容路线——这正是下一块碎片(一致性哈希)要解决的问题。

详解 · K21-11 一致性哈希(Consistent Hashing)

公式组装器给出的 K/N,是 Karger 等 1997 年 MIT 论文(为分布式 Web 缓存而生)的核心结论:把哈希空间首尾相接成 2^32 的环,节点哈希到环上,key 顺时针找最近节点。增删一个节点,只有它与逆时针前驱之间的那一段 key 需要迁移——期望迁移量 K/N。对照台上场景亲手验算:5 台扩到 6 台,mod 5 → mod 6 让 ≈5/6 的 key 归属改变(几乎全搬);一致性哈希只迁 1/6——差距随 N 增大愈发悬殊(N=100 时 mod N 要搬 99%,哈希环只搬 1%)。均匀性问题靠虚拟节点解决:每台物理节点在环上撒 V 个虚拟节点(Dynamo 论文的经典值 V=150;Cassandra 的 vnodes 默认 256),负载方差随 V 收窄——台上"不加虚拟节点"按钮演示的"某节点分到 60%"就是没有它的世界;顺带红利是异构集群可以给强机器配更多虚拟节点。工程谱系:Dynamo、Cassandra、Memcached 客户端分片(Ketama)、Discord 的会话分配。它与复制天然合拍:数据顺时针找"接下来的 R 个节点"存放副本——同一个环同时回答"放哪"与"放几份"。

详解 · K21-12 MapReduce计算模型:Map→Shuffle→Reduce

流水线上那三个方块,对应 Dean & Ghemawat 2004 年 OSDI 论文(Google 内部更早就在跑;开源化身是 Hadoop MapReduce)。编程模型只需两个函数:map(k,v)→emit(k′,v′) 中间对;reduce(k′, [v′…])→按 key 汇聚。用户不写任何网络与容错代码——框架全包,这正是它统治一个时代的原因。Shuffle 是隐藏的主角:map 输出按 key 哈希到各 reducer,网络传输 + 归并排序常占整个作业时间的三分之一以上;台上"跳过 Shuffle"按钮演示的正是没有它 reduce 拿到一锅乱粥。容错哲学与共识截然不同:不做一致性协议,只做"检测 + 重跑"——map 任务失败自动重派到其他节点重算(输入来自 GFS 多副本,总有一份可读);对付掉队节点(straggler)用备份执行(speculative execution):同一任务启动多个副本,谁先完成用谁。台上那句"1 小时跑两遍是 2 小时,10 小时跑一遍跑不完——重跑比不跑强"就是这个取舍的算术。定位与局限:批处理之王,但每个阶段落盘、延迟以分钟计——后来被 Spark(内存 DAG)与流式系统(Flink)接替,但"移动计算比移动数据便宜""故障靠重跑"两条思想留在了所有后继者身上。

第三幕 · 最高潮

Boss 战 · 分区之壁三问

裂账清算 + 三问对决 + 排除裂纹收网 + 脑裂残影

星图台灯火齐灭。分区之壁从四面八方压过来,凝成一面横亘天际的巨壁,壁上每一条裂纹都在发光——那是你这一路刻下的所有"此路不通"。

分区之壁 分区之壁

「我是分区之壁。我不住在你的错误里——错误救不了我。我住在你'以为网络是可靠的'的地方。你每以为多接一台机器就多一份力量,我就胖了一圈。三问。答错一题,壁就厚一分。」

渊账累计 0 笔 · 三问全对后清零

第三幕 · 收束

尾声 · 微服务远眺

RPC/gRPC + 消息队列 + 微服务架构 · 境界突破 · ch22 悬念

壁散尽时天机门大开,远处地平线上浮现出一座由无数微光节点组成的巨型城邦——每个节点都是一个独立服务,彼此用光链相连,城邦上空漂浮着消息流的光河。

诺依曼老祖 诺依曼老祖

「你入门了。不是因为你答对了三问——答对的会忘。是因为你摔过的每一跤,都刻在你手上了。看看那座城邦。你这一路学的 CAP、共识、事务、分片——它们最终长成了这个样子。」

知识图谱点亮 · 微服务远眺

RPC 拆解 → 消息流拆解 → 架构演进 → 11 块碎片点亮 → 境界突破

RPC / gRPC
消息队列
微服务架构
详解 · K21-15 RPC(远程过程调用)与gRPC框架

RPC 的原始幻术出自 Birrell & Nelson 1984 年的论文:把"调用远程机器的函数"伪装成"调用本地函数"。四十年的教训浓缩成一句:这个伪装是危险的——本地调用亚毫秒且必达,远程调用毫秒级且会超时、会丢、会重试后重放;Kleppmann 第 4 章的忠告是"远程调用必须显式暴露这些差异":超时、重试、幂等是调用方的责任,不是框架的恩赐。一次 RPC 的解剖:客户端 stub 序列化参数 → 网络传输 → 服务端 stub 反序列化 → 执行 → 原路返回——每一步都可能失败,所以成熟 RPC 栈都内建负载均衡、超时预算与熔断。gRPC(Google 2015 年开源,前身是内部 Stubby)是当代事实标准,三件套:Protobuf 二进制序列化(比 JSON 更小更快,接口即契约 .proto 文件);HTTP/2 多路复用(一条连接并发跑多个流,告别 HTTP/1.1 队头阻塞);四类通信模式(一元、服务端流、客户端流、双向流)——双向流正是即时通讯与流式代理工具调用的底层形态。S0 台上那五件剪影里的每一件,拆开看都是百万级 QPS 的 RPC 集群:城邦里那些光链,链链皆 RPC。

详解 · K21-16 消息队列与事件驱动架构

消息队列把"调用"变"寄信":生产者把消息写给 broker,消费者异步取走——Kleppmann 第 11 章给它的三个关键词是解耦、削峰、异步。解耦:订单服务不必知道下游谁在订阅,新增一个风控订阅方,上游零改动。削峰:秒杀 10 万 QPS 打进来,队列先接住,下游按自己 5 千 QPS 的节奏消化——城邦上空那条光河就是缓冲池。异步:用户下单立即返回,积分、通知、库存走消息慢慢追。代价是把一套新问题搬进门:消息可能重复(消费方必须幂等——S2 冲突处理的老朋友);可能丢失(生产端要 confirm、broker 要刷盘多副本、消费端要先处理再 ack);可能乱序(Kafka 只保证分区内有序,分区间无序)。两大流派:Kafka 是分布式提交日志(log-based,消息持久保留、可重放,天然适配事件溯源与流处理,消费者自己管 offset);RabbitMQ 是经典队列(消息取走即删,交换机路由灵活,适配任务分发)。选型口诀:要重放、要流式分析选 Kafka;要复杂路由、要任务队列选 RabbitMQ。事件驱动架构(EDA)就是把它推到极致:服务间一切通信皆事件——S1 灵阵里那些光点互发的消息,本质就是它。

详解 · K21-17 微服务架构基础

微服务是把本章全部教训推上架构层的结果:按业务能力把单体拆成独立部署的小服务(Fowler & Lewis 2014 年的定义文章),每个服务自带数据、独立演进、技术栈自由。收益三件:独立扩缩(推荐服务扛 100 倍流量,只扩它);故障隔离(一台崩不拖垮全站——前提是真的做了舱壁隔离);部署提速(小服务小发布)。但 vista 卡上那句"CAP、共识、事务,一个都没少"才是本章的落点:单体的函数调用变成 RPC(K21-15 的超时重试全用上);单体的本地事务变成分布式事务(K21-09 的 2PC 阻塞等着你,成熟做法是 Saga 补偿链);单一数据库变成 N 个库的最终一致(K21-03 的 BASE 世界)。服务多了还要配齐三件套:服务发现(etcd/Consul——底层正是 S5 的 Raft)、熔断限流(把 K21-13 的故障检测搬进调用方)、链路追踪(Dapper/OpenTelemetry,没有 trace 就没有排障)。"微服务不是银弹"——Fowler 自己补写了《Monolith First》:多数团队应先写单体,等边界清晰再拆。城邦很美,入城要交本章全程学过的学费。

复盘 · 分布之壁主线

回看整章裂账的因果链:S1 部分失败教会你"不知道谁坏了";S2 复制教会你"每一份副本都是新的不一致机会"(W+R>N 是强一致的算术门槛);S3 CAP 用七步反证把"全都要"钉死——P 是物理默认值,分区时只能选 C 或 A;S4/S5 共识(Paxos 的多数派交集 3+3−5=1,到 Raft 的随机超时选举)看似破局,实则是主动站到 CP 侧;S6 2PC 让 S1 的断链在最需要事务的时刻复发——阻塞是分区收的利息,一致性级别是 CAP 之内的第二条权衡轴;S7 时钟殿挖出总根:没有全局时间,"谁对"无解,只能自己构造逻辑时钟;S8 转向工程突围:分片缩小爆炸半径、一致性哈希把扩容代价从近 100% 压到 K/N、MapReduce 用重跑买容错;S9 三问清算;S10 把这些权衡装进微服务城邦(RPC 是光链、消息队列是光河)。主线只有一句:分布式系统的灵魂不是更多机器,而是在部分失败与分区约束下做有意识的权衡——权衡链上每个协议都只是一点:Paxos/Raft/2PC 在 CP 侧,无主 Quorum 居中可调,AP 系统在最右。带着这条链进 ch22 语言通神。

交互实验

B+ 树高度

拖动键数与阶,观察 B+ 树高度随键数对数增长。