1.1 分布式系统模型:部分失败、时序与不可能边界
在常见的消息传递模型中,分布式节点通过消息交换信息,各自可能独立失败。网络和处理延迟有没有上界,要由系统模型说明,不能看一次压测很顺就默认永远如此。正确性结论依赖这些假设。
部分失败带来结果未知
客户端调用远程服务后超时,至少存在几种可能:
- 请求没有到达服务端;
- 请求到达,服务端正在处理;
- 业务已提交,响应在返回途中丢失;
- 服务端、网络或客户端某一侧过载;
- 请求仍在排队,稍后还会执行。
超时本身不足以判定业务失败:观察者没有在截止时间内拿到结果。对写请求直接重试,可能把一次成功变成两次副作用。
例如扣款 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 局部故障;
- 节点还能提供健康检查,却无法访问关键磁盘。
设计文档应写出假设
- 节点失败后是否保留持久状态?
- 网络是否可能分区、乱序、重复或延迟消息?
- 协议容忍几台节点失败,为什么?
- 哪些是始终不能破坏的安全性质,哪些是在给定条件下最终应取得的进展?
- 写请求结果未知时,调用方如何查询或重试?
- 时间和顺序来自物理钟、逻辑钟还是日志位置?
下一课专门处理最后一个问题:分布式系统如何表达事件顺序,以及不同一致性模型究竟承诺什么。
参考资料
Dwork, Lynch, Stockmeyer, Consensus in the Presence of Partial Synchrony
Fischer, Lynch, Paterson, Impossibility of Distributed Consensus with One Faulty Process
Gilbert, Lynch, Perspectives on the CAP Theorem