高可用架构设计总览
一句话:高可用不是”多加一台机器”,而是从架构层面消除单点、缩短故障恢复时间(RTO)、减少数据丢失(RPO)——让系统在部分组件故障时仍能提供服务。
概述
你的知识库中高可用内容散落在各处:MySQL 主从复制与高可用、Redis 高可用与集群、K8s 多副本、etcd 多节点等。本篇把它们串成一个统一的高可用架构设计框架。
高可用核心概念
RTO 与 RPO
graph LR
T0[故障发生] -->|RTO| T1[服务恢复]
T0 -->|RPO| T2[数据恢复点]
T0 -.->|数据丢失| T2
T2 -.->|恢复时长| T1
| 概念 | 全称 | 含义 | 典型值 |
|---|
| RTO | Recovery Time Objective | 从故障发生到服务恢复的目标时间 | 15 min / 30 min / 1 h |
| RPO | Recovery Point Objective | 允许的最大数据丢失量(时间维度) | 0 / 1 min / 5 min / 15 min |
| MTBF | Mean Time Between Failures | 平均故障间隔时间 | 越长越好 |
| MTTR | Mean Time To Recovery | 平均恢复时间 | 越短越好 |
| SPOF | Single Point of Failure | 单点故障 | 必须消除 |
关系:可用性 = MTBF / (MTBF + MTTR)。高可用架构通过拉长 MTBF(冗余+消除单点)和缩短 MTTR(自动故障转移+快速恢复)来提升可用性。
SLO 与 RTO/RPO 的关系
| SLO | 约束 |
|---|
| 99.9% | 月度允许 ~43 min 故障 → RTO < 43 min |
| 99.95% | 月度允许 ~21 min → RTO < 21 min |
| 99.99% | 月度允许 ~4.3 min → RTO < 4.3 min(需自动切换) |
| 99.999% | 年度允许 ~5.2 min → RTO < 1 min(需双活) |
高可用设计层次
graph TD
L1[第一层:消除单点] --> L1A[多副本部署]
L1 --> L1B[跨 AZ 部署]
L1 --> L1C[跨 Region 部署]
L2[第二层:快速故障转移] --> L2A[健康检查 + 自动切换]
L2 --> L2B[负载均衡 + 流量摘除]
L2 --> L2C[DNS failover]
L3[第三层:数据可靠性] --> L3A[多副本复制]
L3 --> L3B[定期备份 + 异地备份]
L3 --> L3C[事务日志 + PITR]
L4[第四层:降级与容错] --> L4A[依赖降级]
L4 --> L4B[熔断/限流]
L4 --> L4C[优雅降级]
L5[第五层:混沌验证] --> L5A[故障注入]
L5 --> L5B[红蓝对抗演练]
第一层:消除单点(冗余设计)
冗余模式对比
| 模式 | 架构 | RTO | RPO | 成本 | 适用场景 |
|---|
| 主备(Active-Standby) | 1 主 1 备,备机冷备 | 分钟级 | 取决于同步频率 | 2x | 中间件/DB |
| 主从(Active-Passive) | 1 主 1 从,从机热备 | 秒级 | 秒级 | 2x | MySQL/Redis |
| 双活(Active-Active) | 多节点同时服务 | 接近 0 | 0 | Nx | 无状态服务 |
| 多活(Multi-Active) | 多 Region 同时服务 | 0 | 0 | Nx + 网络 | 全球服务 |
无状态服务高可用(最简单)
# K8s Deployment 多副本 + PDB
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
spec:
replicas: 4 # 至少 2 副本
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 滚动更新时最多不可用 1 个
maxSurge: 1
template:
spec:
affinity:
podAntiAffinity: # Pod 反亲和——分散到不同节点
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: api-service
topologyKey: kubernetes.io/hostname
topologySpreadConstraints: # 跨 AZ 分散
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-service-pdb
spec:
minAvailable: 2 # 至少保证 2 个 Pod 可用
selector:
matchLabels:
app: api-service
有状态服务高可用
第二层:快速故障转移
健康检查与自动切换
# K8s 探针配置
livenessProbe: # 存活探针:失败重启 Pod
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
failureThreshold: 3 # 3 次失败才重启
readinessProbe: # 就绪探针:失败摘除流量
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 1 # 1 次失败就摘除
startupProbe: # 启动探针:慢启动保护
httpGet:
path: /health
port: 8080
failureThreshold: 30
periodSeconds: 10 # 最多等 5 分钟启动
多层故障转移
故障转移层次(从快到慢):
L1: Pod 级(秒级)
├── K8s livenessProbe → 重启 Pod
└── K8s readinessProbe → 摘除流量
L2: 节点级(10-30 秒)
├── 节点 NotReady → Pod 迁移
└── Pod Disruption Budget 保护
L3: AZ 级(分钟级)
├── AZ 故障 → 跨 AZ 流量切换
└── topologySpreadConstraints 保证跨 AZ 分布
L4: Region 级(分钟-小时级)
├── Region 故障 → DNS 切换到备 Region
└── 全局负载均衡(GSLB)
L5: 数据级(取决于 RPO)
├── 主从切换
├── PITR 恢复
└── 异地备份恢复
DNS 故障转移
# 基于健康检查的 DNS 故障转移
# 主 Region: cn-beijing
# 备 Region: cn-shanghai
# Route53 / 阿里云 DNS 配置
dns_failover:
primary:
region: cn-beijing
endpoint: api-beijing.example.com
health_check:
type: HTTPS
resource_path: /health
interval: 10
failure_threshold: 3
secondary:
region: cn-shanghai
endpoint: api-shanghai.example.com
auto_failback: true # 主 Region 恢复后自动切回
failback_delay: 300 # 5 分钟稳定后才切回
第三层:数据可靠性
数据保护层次
L1: 实时复制(RPO ≈ 0)
├── 同步复制(MySQL 半同步 / etcd Raft)
└── 适用于核心数据
L2: 异步复制(RPO = 秒级)
├── MySQL 异步 binlog 复制
├── Redis 主从异步复制
└── 适用于准核心数据
L3: 定期备份(RPO = 小时/天级)
├── 全量备份(每天)
├── 增量备份(每小时)
└── 适用于非核心数据
L4: 异地备份(RPO = 天级,防 Region 级故障)
├── 跨 Region 备份同步
└── 冷备份归档
MySQL 数据保护示例
# MySQL PITR(Point-in-Time Recovery)流程
# RPO 目标:1 分钟
# 1. 每日全量备份
mysqldump --single-transaction --master-data=2 \
--all-databases > backup_$(date +%Y%m%d).sql
# 2. binlog 实时复制到异地
# my.cnf 配置
# [mysqld]
# log_bin=mysql-bin
# binlog_format=ROW
# binlog_expire_days=7
# sync_binlog=1
# 3. 恢复流程
# a. 恢复最近全量备份
# b. 回放 binlog 到指定时间点
mysqlbinlog --start-datetime="2026-08-03 14:00:00" \
--stop-datetime="2026-08-03 14:05:00" \
mysql-bin.000123 | mysql -u root -p
第四层:降级与容错
依赖降级策略
# 降级策略示例
class DegradationManager:
"""服务降级管理器"""
def __init__(self):
self.degradation_level = "normal" # normal / partial / minimal
def check_dependencies(self) -> str:
"""检查依赖健康度,决定降级级别"""
checks = {
"redis": self._check_redis(),
"mysql": self._check_mysql(),
"downstream_api": self._check_downstream(),
"search_engine": self._check_es(),
}
failed = [k for k, v in checks.items() if not v]
if "mysql" in failed:
return "critical" # 核心依赖挂了,整体降级
elif "redis" in failed:
return "partial" # 缓存挂了,降级到直查 DB + 限流
elif "search_engine" in failed:
return "partial" # 搜索挂了,降级到 DB like 查询
elif "downstream_api" in failed:
return "partial" # 下游挂了,返回兜底数据
else:
return "normal"
def apply_degradation(self, level: str):
if level == "partial":
# 关闭非核心功能(搜索/推荐/统计)
self._disable_features(["search", "recommend", "analytics"])
# 开启限流保护
self._enable_rate_limit(threshold=0.7) # 降到 70% 流量
elif level == "critical":
# 只保留核心链路
self._enable_only_core_path()
# 极限限流
self._enable_rate_limit(threshold=0.3)
熔断器模式
import time
from functools import wraps
class CircuitBreaker:
"""熔断器:连续失败 N 次后断开,一段时间后半开探测"""
def __init__(self, failure_threshold=5, recovery_timeout=30):
self.failure_count = 0
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.state = "closed" # closed / open / half_open
self.last_failure_time = None
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
if self.state == "open":
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = "half_open"
else:
raise CircuitBreakerOpen("Circuit breaker is open")
try:
result = func(*args, **kwargs)
if self.state == "half_open":
self.state = "closed"
self.failure_count = 0
return result
except Exception as e:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "open"
raise
return wrapper
高可用架构成熟度模型
| 级别 | 特征 | RTO | RPO | 典型架构 |
|---|
| L1 | 单机房多副本 | 分钟级 | 分钟级 | K8s 多副本 |
| L2 | 跨 AZ 部署 | 分钟级 | 秒级 | 多 AZ + 自动故障转移 |
| L3 | 跨 Region 主备 | 分钟级 | 分钟级 | 主备 Region + DNS 切换 |
| L4 | 跨 Region 双活 | 秒级 | 秒级 | 多活 + 全局负载均衡 |
| L5 | 全球多活 | 接近 0 | 0 | 单元化部署 + 就近接入 |
设计 Checklist
## 高可用架构设计 Checklist
### 消除单点
- [ ] 无单点服务(所有服务 >= 2 副本)
- [ ] 无单点中间件(DB/Cache/MQ 有主从/集群)
- [ ] 无单点网络(多 AZ/多网卡)
- [ ] 无单点 DNS(多 DNS 服务商)
### 故障转移
- [ ] 健康检查覆盖所有服务
- [ ] 自动故障转移机制(K8s 探针 + 自动切换)
- [ ] 跨 AZ 流量切换预案
- [ ] 跨 Region DNS 切换预案
### 数据可靠性
- [ ] 核心数据实时复制(RPO ≈ 0)
- [ ] 定期全量 + 增量备份
- [ ] 异地备份
- [ ] 备份恢复演练(至少季度一次)
### 降级容错
- [ ] 依赖降级策略(非核心依赖可降级)
- [ ] 熔断器保护下游
- [ ] 限流保护自身
- [ ] 优雅降级(返回兜底数据而非报错)
### 容量预留
- [ ] 核心服务峰值 + 50% 余量
- [ ] 弹性伸缩配置
- [ ] 大促/活动预扩容预案
常见问题 / 坑点
| 问题 | 原因 | 解决方案 |
|---|
| 多副本但同节点 | Pod 没配反亲和 | 配 podAntiAffinity + topologySpreadConstraints |
| 故障转移太慢 | 探针参数不合理 | 调优 initialDelay/period/failureThreshold |
| 跨 AZ 但跨 Region | 以为跨 AZ 就够了 | AZ 故障和 Region 故障概率不同,需分层防护 |
| 备份没验证过 | 备份了但没恢复过 | 定期做恢复演练(至少季度一次) |
| 降级策略只在文档里 | 没有自动化执行 | 代码实现降级管理器,自动检测 + 自动降级 |
| 双活但数据冲突 | 双写无冲突解决机制 | 用 CRDT / 最后写胜 / 分片分域 |
关联知识
参考资源
- Google SRE Book: Things That Go Wrong
- AWS Well-Architected Framework: Reliability Pillar
- Martin Kleppmann: Designing Data-Intensive Applications
学习时间
| 内容 | 日期 | 状态 |
|---|
| 高可用架构总览 | 2026-08-03 | 完成:RTO/RPO、冗余设计、故障转移、数据可靠性、降级容错 |
状态