分布式系统常见故障模型有哪些?
简化版
常见故障模型包括崩溃停止、崩溃后恢复、遗漏故障、时序故障、网络分区和拜占庭故障。工程设计必须先说明假设覆盖哪种故障;心跳、重试、复制、共识和校验各自解决的故障不同,没有一种机制能自动覆盖全部情况。
详细版
崩溃停止指节点停止后不再回来;崩溃恢复指节点可能带着持久化状态重启。遗漏故障包括请求、响应或消息丢失;时序故障表现为响应晚于约定期限;网络分区使存活节点无法通信;拜占庭故障则允许节点发送任意、矛盾甚至恶意信息。
生产系统还常遇到灰色故障:节点没有完全宕机,但只对部分调用慢、丢包或返回错误。超时只能说明在截止时间内没得到响应,不能区分节点宕机、网络延迟、请求已执行但响应丢失。因此调用方要用 deadline、幂等和结果查询处理未知状态,集群则用多数派避免单个节点误判。
选方案时写清系统模型:是否假设非恶意故障、网络是否可能无限延迟、磁盘是否可靠、时钟误差上限是多少。之后再设计检测、隔离、恢复和验证。互联网业务通常重点防崩溃、遗漏、时序与分区;跨信任域或高安全系统才会进一步采用 BFT、签名和多方校验。
完整版教学
一、为什么要先定义故障模型
容错算法的正确性总建立在假设上。主备复制能处理主节点崩溃,不代表能处理主节点向两个备节点发送不同数据;CRC 能发现部分数据损坏,不代表能判断哪个业务值正确。若不声明模型,“高可用”就无法验证。
故障模型应说明组件可能怎样坏、网络可能怎样坏,以及哪些行为暂不考虑。工程上不是模型越强越好:防拜占庭故障需要更多副本、通信和密码学验证,若系统所有节点处于同一可信运维域,成本可能不值得。
易错点:没有收到响应是一种观察结果,不是一种故障结论;它可能对应请求未到达、服务崩溃、执行过慢或响应丢失。
二、崩溃停止与崩溃恢复
崩溃停止模型假定节点一旦停止便永久不再参与,算法只需绕开它。真实服务更常见崩溃恢复:进程重启后重新加入,此时内存状态丢失,磁盘状态可能保留,也可能只有部分写入完成。
崩溃前:日志已刷盘 index=120,内存已处理到 index=125
重启后:只能信任持久化到 120 的记录
恢复:校验日志 -> 从 leader 拉取 121..最新 -> 再开放流量
因此恢复协议要区分已提交与仅在内存执行的状态,使用 WAL、校验和、快照和幂等重放。节点进程重新存活不等于已经具备接流量条件。
三、遗漏、时序与响应未知
遗漏故障可以发生在发送端、传输链路或接收端。客户端发出扣款请求后超时,服务端可能完全没收到,也可能已经扣款但响应丢失。若直接重试且服务端不幂等,就会重复扣款。
时序故障是结果在业务 deadline 之后才到达。对实时请求来说,迟到的正确结果也可能没有价值;更危险的是旧响应覆盖新状态。请求应带幂等键、版本或序列号,调用方忽略已经过期的响应,并提供按业务键查询最终结果的接口。
假设超时 200ms,而延迟分布 P99 为 180ms、P99.9 为 800ms,则约千分之一的请求会被当成超时。10000 QPS 下每秒约 10 次未知结果,不能当罕见边界忽略。
四、网络分区和灰色故障
网络分区并不要求网线完全断开。ACL 配错、交换机故障或单向丢包都可能让一组节点彼此可见、另一组不可见。双方都可能认为对方失效,因此仅靠心跳无法安全地产生唯一主节点,需要多数派、任期和 fencing 等协议。
灰色故障更加隐蔽:节点健康检查成功,却对特定分片超时;服务内部某线程池耗尽,但管理端口仍正常;一条网络路径丢包而另一条正常。此时二值 UP/DOWN 状态不足,必须结合分片错误率、长尾延迟、资源队列和多观察点判断。
节点A -> 节点B:可达
节点B -> 节点A:丢包
负载均衡 -> B:健康检查成功
真实业务 -> B/分片7:持续超时
五、拜占庭故障为什么更强
拜占庭节点可以任意行为,包括伪造消息、向不同节点提供矛盾结果或串谋。普通崩溃容错共识通常假设节点不会撒谎,所以不能直接抵抗这种故障。经典 BFT 在异步安全讨论下通常需要 n >= 3f + 1 才能容忍 f 个拜占庭节点。
例如要容忍 1 个任意作恶节点,至少需要 4 个副本;容忍 2 个需要 7 个。除此之外还要认证消息、防重放和定义成员身份。成本远高于常见的 3 副本崩溃容错,因此应根据信任边界选择。
数据静默损坏不一定是恶意行为,但表现可能类似“节点给出错误值”。存储系统会用 checksum、端到端校验和多副本比对发现并修复。
六、不同模型对应哪些机制
| 故障模型 | 常用检测/保护 | 仍未解决的问题 |
|---|---|---|
| 崩溃停止 | 心跳、健康检查、故障转移 | 心跳误判与状态一致性 |
| 崩溃恢复 | WAL、快照、重放、追赶 | 部分持久化与重复副作用 |
| 消息遗漏 | 确认、重试、序号 | 重复投递,需要幂等 |
| 时序故障 | deadline、取消、熔断 | 请求是否已经执行未知 |
| 网络分区 | quorum、任期、fencing | 少数派可用性下降 |
| 拜占庭故障 | 签名、BFT、多方验证 | 高副本和通信成本 |
机制要组合使用。例如重试处理遗漏却制造重复,必须配幂等;心跳发现疑似失败却可能误判,选主必须依赖 quorum;复制保留数据却会复制逻辑错误,还需备份和审计。
七、故障演练怎样覆盖模型
只杀进程只能验证崩溃停止,覆盖不了慢请求、单向丢包、磁盘满和数据损坏。演练矩阵应包含节点退出、重启恢复、延迟注入、按比例丢包、网络分区、磁盘 I/O 错误和时钟偏移,并为每种实验写预期不变量。
以 3 副本存储为例,隔离 1 个副本后应继续提交且无双主;隔离 leader 后应在目标时间内完成选举;旧 leader 恢复后必须以更高任期退位并追赶日志。验证结果看数据和客户端语义,而不只是进程状态。
指标包括故障检测时间、切换时间、未知结果数、重复副作用数、数据差异和恢复积压。实验结束还要确认注入规则被撤销,防止测试故障残留。
八、常见误区与追问
- 误区:超时说明服务端没有执行。 超时只说明调用方没有按时拿到响应,服务端可能已经提交。
- 误区:心跳失败就能证明节点死亡。 网络分区和调度暂停都会造成误判,安全决策要依赖 quorum 或租约条件。
- 误区:三副本可以容忍一个拜占庭节点。 三副本通常用于容忍崩溃,经典 BFT 容忍 1 个任意故障需至少 4 个副本。
- 误区:进程存活就代表节点健康。 灰色故障可能只影响特定线程池、分片、依赖或网络方向。
- 追问:fail-stop 与 crash-stop 有区别吗? fail-stop 还假设其他节点能可靠检测停止;纯异步系统中完美故障检测并不现实。
- 追问:如何处理请求执行结果未知? 用业务幂等键、结果查询和状态机,不把网络错误直接等同业务失败。
- 追问:为什么备份不能替代复制? 复制用于快速可用性和低 RPO,备份用于应对逻辑错误、误删及副本同步损坏,目标不同。
九、加强记忆
按“停、忘、丢、慢、断、乱”记故障模型:节点停止,重启忘掉内存,消息丢失,响应迟到,网络分区,以及拜占庭式任意行为。每说一种故障,都要配对应的检测、保护和未解决边界;尤其牢记超时不等于失败、心跳不等于证明死亡,容错设计必须建立在明确假设和可重复演练上。