跳到内容

第 7 卷 / 第 1 章 / 第 1 课

1.1 分布式系统模型:部分失败、时序与不可能边界 ​

在常见的消息传递模型中,分布式节点通过消息交换信息,各自可能独立失败。网络和处理延迟有没有上界,要由系统模型说明,不能看一次压测很顺就默认永远如此。正确性结论依赖这些假设。

部分失败带来结果未知 ​

客户端调用远程服务后超时,至少存在几种可能:

  1. 请求没有到达服务端;
  2. 请求到达,服务端正在处理;
  3. 业务已提交,响应在返回途中丢失;
  4. 服务端、网络或客户端某一侧过载;
  5. 请求仍在排队,稍后还会执行。

超时本身不足以判定业务失败:观察者没有在截止时间内拿到结果。对写请求直接重试,可能把一次成功变成两次副作用。

例如扣款 op-17 已提交,响应却丢了。客户端换一个请求编号重试,服务端可能再扣一次;复用同一个幂等键,并让服务端持久保存该键与扣款结果的对应关系,才能识别重复操作。去重记录还必须与扣款原子提交,否则两个步骤之间仍有崩溃窗口。状态查询和对账用于找回结果,延长超时只会让人多等一会儿。

先声明系统模型 ​

同步模型 ​

消息传递和进程执行速度满足已知时间界限;若协议还使用物理时钟,也需说明时钟误差假设。一般网络很难长期满足严格上界。

异步模型 ​

不假设消息和处理时间上界。只凭一段时间内没有响应,无法区分节点崩溃与节点或网络很慢。

部分同步模型 ​

一种常用定义是:系统在某个未知时刻之后满足给定的延迟和处理时间界限。另一种定义允许界限始终存在,但协议事先不知道具体数值。Raft、Paxos 家族等工程共识系统通常依赖足够稳定、及时的通信和处理来获得进展。这里谈的是时间假设,与“最终一致性”无关。

模型还应声明故障类型:

  • crash-stop:节点崩溃后不再回来;
  • crash-recovery:节点可带持久状态恢复;
  • omission:消息可能丢失;
  • Byzantine:节点可能发送任意或恶意信息。

只能容忍崩溃故障的协议,不能自动抵御磁盘静默损坏、证书泄露或恶意节点。

Safety 与 Liveness 分开 ​

  • Safety:坏事永远不发生,例如同一日志位置不会提交两个不同值;
  • Liveness:在约定条件满足时,好事最终会发生,例如足够多副本恢复通信和处理后,新写入最终可以提交。

网络分区期间,一个共识系统可以停止写入来维持 safety,却暂时失去 liveness。以三副本多数派协议为例,只有一台正常,即使它的网络没有故障,也不能形成多数派。测试“最终能恢复”不能替代证明“不会提交冲突结果”。

CAP 讨论的是分区期间的响应选择 ​

CAP 中:

  • Consistency 指线性一致性语义;
  • Availability 要求每个到达非失败节点的请求最终返回操作规格允许的结果;对读写寄存器一律回复“服务不可用”不算满足这个要求,它也没有规定毫秒级延迟指标;
  • Partition tolerance 把节点之间可能持续丢失消息纳入故障模型,要求讨论系统在这种条件下还能保留哪些保证。

设寄存器 x 初值为 0,副本 A、B 之间完全断联。A 接受 write(x, 1) 并返回成功,此后客户端才向 B 发起读。线性一致性要求读到 1,但 B 连 A 是否收到过这次写入都不知道。B 等待或拒绝服务,会失去上述可用性;直接返回旧值 0,会违反线性一致性。猜一个新值也没有用,在“根本没有写入”的执行中又会答错。

这就是 CAP 的冲突:在这样的分区中,读写服务不能同时保证线性一致性和所有非失败节点都能完成请求。

实际系统不是永久贴上“CP”或“AP”标签。同一产品可以对元数据写入选择拒绝,对缓存读取返回旧值;即使没有网络分区,延迟、故障恢复和一致性级别仍存在其他权衡。

CAP 也不能替代事务隔离、持久性、因果一致性和延迟设计。

FLP 限定了确定性共识的终止保证 ​

FLP 证明:在完全异步消息系统中,即使消息可靠传递、至多一个进程可能崩溃,也不存在同时保证一致决定、决定值有效,并让每个容许的执行都终止的确定性共识算法。网络无需永久分区;任意长但有限的延迟已经足够造成困难。

它没有说工程系统永远不能达成共识。超时和选主本身不能绕过 FLP;协议还需要最终及时性、更强故障检测器等额外假设来保证进展。随机化算法则改变了终止保证的形式,例如在相应模型下以概率 1 终止。工程共识协议通常在时序不利时保持安全、暂停进展,等条件恢复再继续。

Failure detector 只能怀疑 ​

心跳超时说明“在当前阈值和观测点看来不可达”,不证明节点已经死亡。阈值太短会误判并频繁选主,太长会延迟恢复。

故障检测还必须考虑:

  • Stop-the-world GC 或 CPU 饥饿;
  • 网络单向中断;
  • 进程长时间暂停或虚拟机迁移;
  • 交换机和 DNS 局部故障;
  • 节点还能提供健康检查,却无法访问关键磁盘。

设计文档应写出假设 ​

  • 节点失败后是否保留持久状态?
  • 网络是否可能分区、乱序、重复或延迟消息?
  • 协议容忍几台节点失败,为什么?
  • 哪些是始终不能破坏的安全性质,哪些是在给定条件下最终应取得的进展?
  • 写请求结果未知时,调用方如何查询或重试?
  • 时间和顺序来自物理钟、逻辑钟还是日志位置?

下一课专门处理最后一个问题:分布式系统如何表达事件顺序,以及不同一致性模型究竟承诺什么。

参考资料 ​

Built with VitePress | Software Systems Atlas