跳转到正文
zeno's blog
返回

分布式基础(四):容灾-容错与灾难恢复

专题: 分布式基础

Table of contents

Open Table of contents

TL;DR

容灾是伞状概念,包含容错(Fault Tolerance)和灾难恢复(Disaster Recovery)。容错要求故障发生时零中断继续服务(用冗余掩盖故障);灾难恢复关注灾难后如何恢复(用备份和复制缩短恢复时间)。核心思想:故障不是”是否发生”而是”何时发生”——系统设计必须将故障视为常态。


概念区分

维度容错(FT)高可用(HA)灾难恢复(DR)
核心目标故障时继续运行,用户无感知最大化正常运行时间灾难后恢复系统
故障规模组件级(单盘、单节点)服务级灾难级(机房、Region)
中断容忍零中断允许极短中断(秒级)允许中断(分钟到小时)
衡量指标故障掩盖率SLA uptime %RTO + RPO

三者的关系:FT 是 HA 的实现手段之一,HA 关注”别挂”,DR 关注”挂了怎么办”。

故障类型

类型典型案例检测难度
硬件故障磁盘损坏、内存 bit flip、电源故障低~中
软件故障Bug、内存泄漏、配置错误、依赖故障中~高
网络故障网络分区、延迟抖动、DNS 故障
人为错误误操作 DROP TABLE、变更引入 regression最常见(约 70%)

Google SRE 数据:人为错误是生产故障首要原因。 所以容错不能只考虑硬件软件,操作审计、变更管控、灰度发布等流程性措施同样是容错的一部分。

冗余策略

策略资源利用率切换时间适用层
Active-Active0(无需切换)无状态服务
Active-Passive低(50%浪费)秒~分钟有状态服务(DB)
N+1较高取决于调度计算节点池
2N低(50%浪费)极短关键基础设施(电源)

故障域(Blast Radius)

架构设计的核心任务之一是控制故障影响范围

故障域层级:Server < Rack < AZ < Region

副本必须跨故障域分布。3 副本放同一 Rack,交换机一挂全完。

优雅降级

核心:保住核心功能,牺牲非核心功能。这是产品决策,不是纯技术问题。

正常 → 功能降级(关闭推荐/评论)
     → 数据降级(返回缓存/默认值)
     → 体验降级(静态页面)
     → 流量降级(限流/排队)
     → 完全不可用 ← 要避免到这一步

降级必须提前编码,降级开关通过配置中心秒级生效,不能在故障时临时决定。

混沌工程

不是”随机搞破坏”,而是科学实验方法:受控故障注入,验证系统行为是否符合预期。

原则:建立稳态假设 → 用真实世界事件模拟 → 在生产环境运行 → 持续自动化 → 最小化 blast radius。

工具:Chaos Monkey(Netflix,随机杀实例)、Litmus/Chaos Mesh(K8s 原生)、Toxiproxy(网络故障模拟)。

GameDay:有组织的故障演练。计划→执行→复盘→修复。关键系统每季度至少一次。

灾难恢复(DR)

RPO 和 RTO

最后一次备份 ──RPO(丢多少数据)──▶ 灾难 ──RTO(停多久)──▶ 恢复完成

RPO/RTO 越小成本越高。这是业务决策——业务方需要回答”丢 1 小时数据的代价是多少钱”。

DR 策略分级

策略RTORPO成本适用场景
Backup & Restore小时~天小时~天最低非关键系统
Pilot Light分钟~小时接近 0中等重要性
Warm Standby分钟级接近 0重要业务
Multi-Site Active-Active秒级~00最高核心交易系统

备份验证

不验证的备份等于没备份。 GitLab 2017 年事故——5 种备份全部失败,靠偶然快照救回。

备份验证必须包括:完整性校验 + 定期恢复到隔离环境 + 时效性检查 + 失败告警。

PostgreSQL 备份:小库用 pg_dump;大库用 pg_basebackup + WAL 归档支持 PITR;推荐 pgBackRest(增量、并行、远程、验证)。

跨 Region 复制

复制方式RPO写性能影响适用场景
同步0同城/同 AZ
异步>0跨 Region
半同步接近 0同城双中心

物理限制:北京到上海 ~5ms RTT,北京到美东 ~200ms RTT。跨 Region 同步复制不可行。

关键设计模式

Circuit Breaker

快速失败比慢速等待好。三态:Closed(正常)→ Open(全部拒绝)→ Half-Open(试探)。

关键参数:失败阈值(滑动窗口内错误率 50% 且至少 20 个请求)、Open 持续时间(30-60s)、Half-Open 成功阈值。

Bulkhead(隔舱)

不同下游依赖使用独立线程池/连接池。API-B 超时耗尽自己的池子,API-A/C/D 完全不受影响。

Timeout + Retry + Backoff + Jitter

每个外部调用都必须设 timeout,没有超时 = 资源泄漏定时炸弹。

组合使用:超时分层递减 + 限制重试次数(2-3 次)+ 指数退避 + Jitter 打散 + 总超时兜底。

不是所有错误都该重试:503/超时可重试;400/401/404 不可重试;非幂等操作需要 idempotency key。

幂等性

容错体系的基石——没有幂等性,重试就是定时炸弹。

实现方式:幂等键(客户端生成 UUID)、数据库唯一约束(ON CONFLICT DO NOTHING)、状态机(操作只在特定状态执行)、乐观锁(version 字段)。

故障场景速查

场景应对RTO
单节点故障K8s 自动重调度秒级
单 AZ 故障流量切其他 AZ分钟级
Region 故障DR 站点接管分钟~小时
网络分区Quorum 多数派继续服务秒~分钟
数据损坏PITR 恢复到损坏前小时级
级联故障Circuit Breaker + Bulkhead + 限流取决于隔离速度

级联故障是最危险的:Service A 超时 → 线程池耗尽 → Service B 调 A 超时 → B 也耗尽 → 继续蔓延。防止组合拳:Circuit Breaker + Bulkhead + Timeout + 限流 + 降级。

Pitfalls

  1. 备份从未恢复测试 — 备份天天在跑但真恢复时才发现损坏/不完整。对策:每月恢复演练到隔离环境
  2. DR 方案只在文档里 — 手册中的 IP/密码/工具早已过时。对策:每季度 DR 演练
  3. 级联故障 — 非核心服务 DB 慢查询拖垮整个系统。对策:外部调用设 timeout + Circuit Breaker
  4. 脑裂 — 网络分区后两个”主”同时写,数据分叉。对策:quorum + STONITH fencing
  5. 超时设置不合理 — 每层都 30s timeout,下游卡住上游全部线程阻塞。对策:超时分层递减 + context deadline 传递
  6. 重试风暴 — 所有客户端同时重试,3 倍流量打挂刚恢复的服务。对策:Backoff + Jitter + Circuit Breaker + 重试预算
  7. 只有复制没有备份 — 误删 DROP TABLE 被忠实地复制到所有副本。对策:复制应对硬件故障,备份应对数据损坏/误删(PITR)
  8. 监控盲区 — 复制延迟从 1s 悄悄涨到 1 小时无人知。对策:复制延迟监控 + 分级告警

生产 Checklist

基础容错

数据保护

灾难恢复

混沌工程


分享这篇文章:

上一篇
Rust Web:Actix-web HTTP Server 指南
下一篇
分布式基础(三):高可用-通过冗余和自动故障转移消除单点故障