本页目录

分布式 I · 时钟与一致性模型

对标:MIT 6.824 / Designing Data-Intensive Applications(DDIA)| 前置:net 线、db-03(事务、隔离) 分布式系统是"用多台机器协作,对外装成一台可靠机器"的艺术——为了扩展性(一台不够)和容错(一台会挂)。但它引入了单机世界没有的三个恶魔:没有全局时钟、消息会丢会延迟、机器会独立崩溃。这一页建立分布式的世界观:时间为什么不可靠、"一致"到底有几种含义、以及 CAP 定理逼你做的那个残酷取舍。

学习层:两个事件同一时刻发生,系统能证明谁先吗?

具体谜题:标量时钟为什么会漏掉并发?

节点 A 向 B 发送 m;B 在收到 m 前做本地事件 b1;节点 C 独立做 c1 后向 A 发送 n。A、B、C 的本地时钟都从 0 开始。请预测 b1 与 c1 是否有因果关系,并比较只看 Lamport 标量与使用向量时钟时,系统能否识别这两个事件并发。

先预测,再展开:选择一对事件并预测“先于 / 后于 / 并发”;再判断为什么 Lamport 值相等或不相等都不能单独证明并发,而向量时钟可以给出双向不可比的证据。

最小心智模型:偏序而非全序

分布式事件由本地程序顺序和消息发送—接收边构成有向无环图。系统拥有因果偏序,却没有免费的全局实时钟;复制状态的一致性模型是在这个偏序上规定读写必须满足的可见性约束。

形式机制与不变量

Lamport 关系 \(a\to b\) 是本地顺序、消息边及其传递闭包;标量时钟保证 \(a\to b\Rightarrow L(a)<L(b)\),但逆命题不成立。向量时钟 \(V_i\) 在本地事件递增,接收消息取逐分量最大值后再递增;\(V(a)<V(b)\) 当且仅当已知 \(a\to b\)。线性一致性还要求每个操作看起来落在一个满足实时顺序的单一全序中。

反例与失效边界

向量时钟记录因果,不提供物理时间,也不能让网络消息变快;节点数变大时向量长度与元数据成本上升。最终一致允许暂时读到旧副本,CAP 的“不可同时满足”还依赖分区容错与可用性的定义,不能把所有延迟都直接叫作网络分区。

迁移任务:为复制服务选择承诺

为 Medusa 的分析卡片和预测状态分别写出允许的陈旧窗口、读修复和冲突策略;再将 db-03 的事务提交事件标为因果边,说明它如何影响 dist-02 的日志顺序。这里的时钟实验不替代 L06 Raft 的真实网络模拟,而是先让你能检查 trace 是否违反因果。

交互实验:Lamport 与向量时钟的因果账本

无 JavaScript 时的静态读法:固定事件为 A:a1、A:send(m)、B:b1、C:c1、B:recv(m)、C:send(n)、A:recv(n)、B:b2。典型向量分别为 a1=[1,0,0]、send(m)=[2,0,0]、b1=[0,1,0]、c1=[0,0,1]、recv(m)=[2,2,0]、send(n)=[0,0,2]、recv(n)=[3,0,2]、b2=[2,3,0]。b1 与 c1 双向不可比,故并发;send(m)→recv(m),故有因果。Lamport 标量可能把 recv(m) 与 recv(n) 都记为 3,却无法据此判断其关系。

事件 Lamport 向量 [A,B,C] 说明
A:send(m) 2 [2,0,0] 消息边起点
B:b1 1 [0,1,0] 与 C:c1 并发
B:recv(m) 3 [2,2,0] 因果接收
A:recv(n) 3 [3,0,2] 与 recv(m) 不可比

1. 分布式的三个残酷现实

单机的确定性在分布式里全部失效——"分布式计算的八大谬误" 的核心三条:

  1. 网络不可靠:消息会丢失、延迟、乱序、重复(net-01 的 IP 尽力而为,放大到系统级)。
  2. 没有共享时钟:每台机器的时钟独立漂移,你无法可靠判断两台机器上的事件谁先谁后。
  3. 节点独立故障:一台崩溃时,别的节点分不清它是"挂了"还是"只是慢/网络断了"——这个"无法区分崩溃与缓慢"是分布式最深的困难根源。

这三条推出分布式的一切复杂性。单机程序员的直觉(顺序、时间、"要么成功要么失败")在这里全要重建。

2. 时间与因果:Lamport 的洞察

Lamport 逻辑时钟:三节点收发消息 + max+1 规则,因果先后⇒时间戳递增。

图 dist-01.3Lamport 逻辑时钟:三节点收发消息 + max+1 规则,因果先后⇒时间戳递增。

既然物理时钟不可靠,怎么给分布式事件排序?Lamport 的洞察:我们真正需要的不是"物理时间",而是因果顺序(谁导致了谁)。

读法:分布式里"时间"应理解为"因果依赖"而非"钟表读数"(🔗 与物理站狭义相对论的"同时性相对、因果序绝对"惊人同构——两门学科独立得出"因果比时间根本")。真实系统也用混合逻辑时钟、或 Google Spanner 的 TrueTime(用原子钟 + GPS 把时钟不确定性界定在几毫秒内,花硬件买回近似全局时钟)。

3. 复制与一致性模型:从强到弱的光谱

一致性光谱:线性→顺序→因果→最终,强度 vs 性能。

图 dist-01.2一致性光谱:线性→顺序→因果→最终,强度 vs 性能。

为容错和扩展,数据要复制到多个节点。但副本间怎么保持"一致"?——"一致性"不是一个概念,是一条从强到弱的光谱,每档是"正确性 vs 性能/可用性"的不同取舍:

一致性模型 承诺 代价
线性一致(强) 系统表现得像单一副本、操作看似瞬时生效、全局唯一顺序 慢(每次操作要协调)、分区时不可用
顺序一致 所有节点看到相同的操作顺序(但不必是实时的) 略松
因果一致 有因果关系的操作顺序一致,并发的可不同 可用性好、够多数应用
最终一致(弱) 停止写入后,副本最终收敛到一致 最快最可用,但中间可读到旧值

这直接接 db-03 的隔离级别——都是"用一致性强度换性能"的旋钮,只是从单机事务放大到跨机复制。理解"一致性是光谱不是开关",是分布式设计的核心成熟度。

4. CAP 定理:那个残酷的取舍

CAP 定理:网络分区时 C/A 二选一,标 CP(etcd/Postgres) vs AP(Cassandra/DNS)。

图 dist-01.1CAP 定理:网络分区时 C/A 二选一,标 CP(etcd/Postgres) vs AP(Cassandra/DNS)。

CAP 定理:一个分布式系统在网络分区(P,Partition,消息不通)发生时,无法同时保证一致性(C)和可用性(A),只能二选一。

为什么【直觉证明】:网络分区把节点切成两组互不通信。此刻来了个写请求:

关键澄清:CAP 常被误读。分区不发生时,C 和 A 可以兼得;CAP 说的是分区发生时的强制取舍。而分区在真实网络中必然偶尔发生,所以每个分布式系统都隐含选了 CP 或 AP:

方法论:"这个系统在网络分区时该拒绝服务还是返回可能过期的数据?"——这个问题的答案决定了你的整个架构。对 Medusa:分析数据晚一点没关系(可 AP/最终一致),但如果有涉及一致性的关键写,就要 CP。没有正确答案,只有适配业务的取舍。(更精细的 PACELC 定理补充:即使无分区,也要在延迟 L 和一致性 C 间取舍。)

5. 练习与要点

例 1(逻辑时钟手算) 三节点互发几条消息,手算每个事件的 Lamport 时间戳,验证"因果先后 ⇒ 时间戳递增"——理解"用整数排因果",也理解它为什么不能反推(时间戳小不代表因果先)。

例 2(CAP 归类) 给三个系统(银行转账、社交媒体点赞数、DNS)各判该选 CP 还是 AP 并说理由——把 CAP 从定理变成设计判断。点赞数少一个无所谓(AP)、转账不能出错(CP)。

例 3(一致性够不够) Medusa 的"分析卡片展示"和"预测状态更新"分别需要哪档一致性?——大多数读多写少的分析场景最终一致就够,别为不需要的强一致付性能税。\(\blacksquare\)


下一页:分布式 II——共识与 Raft:一群会宕机的机器,怎么对"发生了什么"达成一致。这是分布式的皇冠。