支付系统的高可用架构是确保金融服务连续性的核心支柱。不同于普通互联网应用,支付系统的不可用不仅意味着收入损失,还可能导致资金延迟、合规违规、客户流失甚至监管处罚。本章从可用性目标出发,系统覆盖多活部署、数据同步、故障切换、降级策略、容量规划、混沌工程与灾备演练等关键领域,辅以具体数值计算、对比表格和实际案例。
支付系统通常以 "9 的个数" 来衡量可用性等级(Availability Tier)。一个系统声称 "四个 9" 意味着其在一年内的计划外停机时间不超过 52.56 分钟。
| 可用性等级 | 年停机时间 | 月停机时间 | 周停机时间 | 典型场景 |
|---|---|---|---|---|
| 99%(2 个 9) | 87.6 小时 | 7.3 小时 | 1.68 小时 | 内部测试系统 |
| 99.9%(3 个 9) | 8.76 小时 | 43.8 分钟 | 10.1 分钟 | 非核心业务系统 |
| 99.99%(4 个 9) | 52.56 分钟 | 4.38 分钟 | 1.01 分钟 | 支付核心系统 |
| 99.999%(5 个 9) | 5.26 分钟 | 25.9 秒 | 6.05 秒 | 交易所撮合、SWIFT |
| 99.9999%(6 个 9) | 31.56 秒 | 2.63 秒 | 0.6 秒 | 央行支付系统 |
以某跨境支付平台为例,其承诺商户的可用性 SLA 为 99.99%,架构上需要逐层保障:
| 架构层 | 单层可用性 | 冗余方式 | 单层失效影响 |
|---|---|---|---|
| CDN / DNS | 99.999% | 多 DNS 厂商 | 用户无法访问前端 |
| API 网关 | 99.995% | 多集群 + 健康检查 | 请求无法路由 |
| 业务服务层 | 99.99% | 多 AZ 部署 + 容器编排 | 交易处理中断 |
| 数据库层 | 99.995% | 主从 + 异地灾备 | 数据不可读写 |
| 消息队列 | 99.99% | 集群 + 消息持久化 | 异步任务积压 |
| 外部通道 | 99.9% | 多通道 + 自动切换 | 交易无法路由到下游 |
系统整体可用性的理论值由各层乘积得出。假设每层为独立依赖关系:
代入上表最低值:,约为 99.969%,未达到 99.99% 目标。这意味着必须大幅提升通道层(通过多通道冗余)和消息队列层的可用性。
关键结论:单靠各层独立冗余还不够,需要引入跨层故障隔离和多活架构才能真正达到 99.99%。
以日均处理 $1 亿交易额、费率 0.5% 的平台为例:
这就是为什么头部支付公司愿意投入数千万建设多活架构——哪怕将可用性从 99.99% 提升到 99.999%,对于百亿级平台来说,边际收益远高于建设成本。
支付系统的高可用架构通常经历三个阶段:
阶段一:单机房主备 阶段二:同城双活 阶段三:异地多活
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 主机房 │ │ 备机房 │ │ 机房A │ │ 机房B │ │ 北京机房 │ │ 上海机房 │
│ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │
│ │Active│ │ │ │Passive│ │ │ │Active│ │ │ │Active│ │ │ │Active│ │ │ │Active│ │
│ │App │ │ │ │App │ │ │ │App │ │ │ │App │ │ │ │App │ │ │ │App │ │
│ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │ │ └──────┘ │
│ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │ │ ┌──────┐ │
│ │Active│ │ │ │Passive│ │ │ │Active│ │ │ │Active│ │ │ │Active│ │ │ │Active│ │
│ │DB │ │ │ │DB │ │ │ │DB │ │ │ │DB │ │ │ │DB │ │ │ │DB │ │
│ └──────┘ │ │ └──────┘ │ │ └──┬───┘ │ │ └──┬───┘ │ │ └──┬───┘ │ │ └──┬───┘ │
│ ┌───┐ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ MQ│ │ │ │ │ 同步复制 │ 同步复制 │ │ 异步复制 │ 异步复制 │
│ └───┘ │ │ │ │ └───────┴───────┘ │ │ └────┴───┴──┘ │
└──────────┘ └──────────┘ └───────────────────────────┘ └──────────────────────────┘
RTO: 30-60min RTO: < 1min RTO: < 5min
RPO: < 5min RPO: 0(同步复制) RPO: < 30s(异步复制)
| 维度 | 单机房主备 | 同城双活 | 异地多活 |
|---|---|---|---|
| 故障切换时间(RTO) | 30-60 分钟 | < 1 分钟 | < 5 分钟 |
| 数据丢失量(RPO) | < 5 分钟 | 零(同步复制) | < 30 秒 |
| 资源利用率 | < 50%(备机闲置) | 70-85% | 85-95% |
| 建设成本 | 1x | 2-3x | 5-10x |
| 运维复杂度 | 低 | 中 | 高 |
| 应对灾难类型 | 单机故障 | 机房级故障 | 区域级灾难 |
| 适用场景 | 创业期、低交易量 | 中型平台、单城市 | 大型平台、跨国业务 |
同城双活中最核心的挑战是 "写冲突"。以支付为例,同一笔订单可能从两个机房同时发起操作。常见的路由策略:
策略一:按用户 ID 哈希路由
user_id = 12345678
hash_value = hash(user_id) % 10 // 0-9
if hash_value < 5:
route_to_az_a()
else:
route_to_az_b()
存在的问题:如果用户量大、哈希均匀,每笔交易的读取可以就近完成,但跨机房的关联查询(如查某个商户的所有交易)仍需访问两个库。
策略二:按商户 ID 路由(推荐做法)
将整个商户的所有交易路由到同一机房,避免跨机房查询:
merchant_id = "merchant_001"
hash_value = hash(merchant_id) % 10
// 同一商户的所有操作都在同一机房
// 跨商户操作(如资金归集)走异步消息
策略三:读写分离 + 数据库层面双写
// 读请求 — 就近路由
GET /api/order/123 → 当前机房
// 写请求 — 主库写入 + 同步复制
POST /api/order/123 → 写入主库 → 同步复制到从库
// 特殊操作 — 全局锁(如冻结、解冻操作)
POST /api/freeze/123 → 全局分布式锁 → 写入所有机房
实际案例:某支付平台双活部署时,发现 70% 的查询都是按商户 + 时间范围查询。通过将同一商户的数据按时间分表后放在同一机房,跨机房查询从 23% 降低到 3%,查询延迟从 45ms 降低到 5ms。
| 灾难类型 | 影响半径 | 机房间距要求 | 恢复策略 |
|---|---|---|---|
| 电力故障 | 单建筑 | 同城市不同区(> 5km) | 同城双活切换 |
| 光纤中断 | 单机房 | 不同供电网格(> 10km) | 同城双活切换 |
| 地震 | 50-200km | 不同省份(> 500km) | 异地多活切换 |
| 洪水 | 区域级 | 不同流域(> 1000km) | 异地多活切换 |
| 大范围网络中断 | 区域级 | 不同运营商骨干网 | 异地多活切换 |
| 维度 | 同步复制(Synchronous) | 异步复制(Asynchronous) | 半同步复制(Semi-sync) |
|---|---|---|---|
| 事务一致性 | 强一致 | 最终一致 | 多数一致 |
| RPO | 零 | 可能丢失 | 极小(通常 < 1 秒) |
| 写入延迟 | 增加 1-5ms | 几乎无影响 | 增加 0.5-2ms |
| 可用性影响 | 从库不可用时主库阻塞 | 无影响 | 从库不可用降级为异步 |
| 跨机房带宽要求 | 高(10Gbps+) | 中(1Gbps+) | 中 |
| 适用距离 | 同城(< 50km) | 异地(任何距离) | 同城 + 部分异地 |
假设某支付平台日均处理 500 万笔交易,每笔交易产生 10 条数据库写入操作:
使用同步复制(同城,5Gbps 专线,延迟 0.5ms):
单次同步复制增加延迟 0.5ms,总延迟影响:
对整体吞吐影响可以忽略。
使用同步复制(异地,1000km,延迟 15ms):
单次同步复制增加延迟 15ms,总延迟影响:
这意味着数据库每秒要花 8.7 秒等待跨地域复制,吞吐量严重下降。这就是为什么异地多活通常采用异步复制。
支付场景中,数据一致性是不可妥协的。即使使用异步复制,也需要确保最终一致性:
方案一:业务层面"对账补偿"
// 事务在主库提交
BEGIN TX;
INSERT INTO orders (id, amount, status) VALUES (1, 100, 'SUCCESS');
UPDATE account SET balance = balance - 100 WHERE id = 123;
COMMIT;
// 异步复制到从库(可能有延迟)
// 对账服务每 5 分钟运行一次:
SELECT * FROM orders o
LEFT JOIN orders@remote o2 ON o.id = o2.id
WHERE o2.id IS NULL; // 找到未同步的记录
// 对缺失记录进行补偿同步
方案二:消息队列 + 最终确认
// 主库写入后,同时发送 MQ 消息
INSERT INTO orders (...) VALUES (...);
publish('order_sync', {order_id: 1, ...});
// 从库消费消息
subscribe('order_sync', (msg) => {
// 幂等写入
INSERT IGNORE INTO orders (...) VALUES (...);
});
方案三:分布式事务(TCC)
对于强一致的场景(如账户扣款与冻结),使用 TCC 模式:
| 阶段 | 操作 | 描述 |
|---|---|---|
| Try | 冻结资金 | 在 A、B 两个机房都预占资金 |
| Confirm | 确认扣款 | 将冻结资金转为实际扣款 |
| Cancel | 释放资金 | 回滚冻结,恢复余额 |
真实场景:某支付平台在跨机房部署初期,发现数据同步延迟最高达到 2 分钟。这导致商户查到交易状态不一致(一个机房显示成功,另一个显示处理中)。解决方案是对关键状态增加"版本号"字段,每次更新时比较版本号,确保旧数据不会覆盖新数据。
故障检测需要在"误报"和"漏报"之间取得平衡。检测太快可能误切(因瞬时抖动),检测太慢则影响 RTO。
| 检测策略 | 检测周期 | 判定标准 | 误报率 | 平均检测时间 |
|---|---|---|---|---|
| TCP 探活 | 1 秒 | 连续 3 次失败 | 高 | 3 秒 |
| HTTP 健康检查 | 5 秒 | 连续 2 次非 200 | 中 | 10 秒 |
| 业务指标监控 | 10 秒 | 成功率 < 95% | 低 | 10-30 秒 |
| 多节点共识(Quorum) | 1 秒 | 半数以上节点判定失败 | 极低 | 3-5 秒 |
推荐做法:多级检测组合使用:
检测流程:
┌─────────────┐
│ TCP 探活 │ ← 每 1 秒探测一次
│ (超时 < 1s) │
└──────┬──────┘
│ 连续 3 次失败
▼
┌─────────────┐
│ HTTP 健康检查│ ← 从独立探测节点发起
│ (/healthz) │
└──────┬──────┘
│ 连续 2 次非 200
▼
┌─────────────┐
│ 业务指标检查 │ ← 检查过去 1 分钟成功率
│ (成功 < 95%)│
└──────┬──────┘
│ 判定为故障
▼
┌─────────────┐
│ 触发自动切换 │ ← 通知所有接入层
└─────────────┘
以一个 99.99% SLA 的系统为例,故障切换的 RTO 目标为 5 分钟。以下是各环节的时间分解:
| 环节 | 基准时间 | 优化后时间 | 优化措施 |
|---|---|---|---|
| 故障检测 | 30 秒 | 5 秒 | 多级检测 + 仲裁机制 |
| 确认决策 | 10 秒 | 3 秒 | 自动化决策引擎 |
| DNS 切换 | 60 秒 | 10 秒 | 提前设置低 TTL(30s) |
| 负载均衡切换 | 30 秒 | 5 秒 | 健康检查 + 主动摘除 |
| 数据库切换 | 120 秒 | 60 秒 | 预配置 HA 集群 |
| 消息队列切换 | 30 秒 | 10 秒 | MQ 集群自动选主 |
| 预热与验证 | 90 秒 | 30 秒 | 保持备机常热 |
| 总计 | 370 秒 | 123 秒 | - |
优化后 RTO 约 2 分钟,远优于 5 分钟目标。
┌─────────────────────────────────────────┐
│ │
▼ │
┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ 正常状态 ├────>│ 故障检测 ├────>│ 切换中 │ │
│ (Green) │ │ (Yellow) │ │ (Orange) │ │
└────┬────┘ └──────────┘ └─────┬────┘ │
│ │ │
│ 健康恢复 │ 切换完成
│ ▼ │
│ ┌──────────┐ │
└───────────────────────────│ 降级运行 ├──┘
│ (Red) │
└──────────┘
│
│ 故障恢复+数据同步完成
▼
┌──────────┐
│ 回切中 │
│ (Orange) │
└────┬─────┘
│ 回切完成
▼
┌──────────┐
│ 正常状态 │
│ (Green) │
└──────────┘
关键规则:
多活架构中最危险的故障是"脑裂"——两个机房都认为对方已死,同时接管写入,导致数据不一致。
解决方案:使用仲裁节点(Witness)或分布式锁(etcd/ZooKeeper):
// 伪代码:基于 etcd 的租约机制
// 只有持有租约的机房可以执行写操作
function acquireLease(campusName):
lease = etcd.grant(10) // 租约 10 秒
success = etcd.put("/active-campus", campusName, lease)
if not success:
// 另一个机房已持有租约
current = etcd.get("/active-campus")
if current == campusName:
// 自己持有,续期
etcd.keepAlive(lease)
else:
// 对方持有,本机房只能读
setReadOnly()
降级是在系统部分能力受损时,通过牺牲非核心功能来保障核心支付链路畅通。
| 等级 | 描述 | 影响范围 | 典型触发条件 |
|---|---|---|---|
| L0 | 正常运行 | 无 | - |
| L1 | 非核心功能降级 | 报表、通知延迟 | 单机房故障 |
| L2 | 辅助功能关闭 | 无风控初审、无对账 | 双机房异常 |
| L3 | 核心功能简化 | 仅支持基础收付款 | 数据库降级 |
| L4 | 只读模式 | 无法发起新交易 | 极端灾难 |
风控降级(L1 → L2):
| 降级级别 | 风控规则 | 审批流程 | 延迟影响 |
|---|---|---|---|
| L0 | 全部规则生效(200+ 条规则) | 实时审批 | 50ms |
| L1 | 仅核心规则(50+ 条规则) | 批量预审 | 20ms |
| L2 | 仅规则引擎简易模式(10 条规则) | 后审 | 5ms |
| L3 | 关闭风控(仅保留黑名单检查) | 无 | 1ms |
通道降级(L1 → L3):
通道矩阵(假设 5 个支付通道):
通道状态矩阵:
主通道A 备通道B 备通道C 备通道D 备通道E
成功率 98.5% 97.2% 96.8% 95.1% 94.3%
成本费率 0.38% 0.35% 0.32% 0.28% 0.25%
延迟(ms) 80 120 200 350 500
支持卡种 Visa/MC Visa MC Amex JCB
L0: 按最优成功率+成本混合路由
L1: 去掉通道D、E(成本优先,牺牲成功率)
L2: 仅保留通道A、B(成功率优先)
L3: 仅保留通道A(最低可用保障)
数据库降级(L2 → L3):
| 降级措施 | 收益 | 风险 |
|---|---|---|
| 关闭历史表查询 | 减少 30% 读负载 | 无法查历史流水 |
| 延迟批处理任务 | 减少 20% 写负载 | 对账延迟 1h |
| 只保留当日表 | 减少 50% 数据量 | 无法查询昨日交易 |
| 切换到备库读 | 主库专注写入 | 秒级数据延迟 |
| 关闭 ES 搜索 | 减少 15% 资源 | 检索功能暂不可用 |
输入: 当前系统状态指标
├── 各机房 CPU/内存/磁盘
├── 各通道成功率
├── 数据库连接池使用率
├── MQ 队列积压量
├── 平均响应延迟 P99
└── 错误率
决策引擎:
1. 比较指标与阈值
2. 触发条件匹配:
- 错误率 > 5% AND P99 > 5s → L2
- 数据库连接池 > 90% → L2
- 通道成功率 < 80% → L1
3. 选择影响最小的降级方案
4. 输出降级指令(携带自动/手动标识)
降级恢复:当系统指标恢复后,按照逆序逐级恢复(L3→L2→L1→L0),每一步有 60 秒观察期验证恢复是否稳定。
方法一:基于历史峰值的规划
以某支付平台 2025 年双 11 的数据为例:
| 指标 | 日常平均值 | 日常峰值 | 双 11 峰值 | 年增长率 |
|---|---|---|---|---|
| TPS | 1,200 | 3,500 | 15,000 | 120% |
| 日交易量 | 1.2 亿笔 | 3.5 亿笔 | 12 亿笔 | 100% |
| 消息体大小 | 2KB | 2KB | 2KB | - |
| 带宽占用 | 200Mbps | 600Mbps | 2.5Gbps | - |
容量规划公式(基于峰值+冗余系数):
其中:
方法二:基于资源利用率的推算
假设数据库集群的单节点能支撑 3,000 TPS:
其中 为单节点最大安全利用率(通常取 60%):
这已经是一个非常庞大的集群——需要分库分表来支撑。实际设计中会拆分为 32 个分片,每个分片 2 个节点(主+备),共 64 个数据库节点。
| 场景 | 伸缩策略 | 触发条件 | 伸缩时间 | 预期效果 |
|---|---|---|---|---|
| 业务量增长 | 水平扩容 | CPU > 70% 持续 5 分钟 | 10-15 分钟 | 增加 Pod 副本数 |
| 突发流量 | 快速弹起 | TPS 瞬时增长 3 倍 | 30-60 秒 | 弹起预热后的 Pod |
| 大促准备 | 提前扩容 | 计划内(如双 11) | 提前 1 周 | 扩容到预估 2 倍容量 |
| 资源回收 | 缩容 | CPU < 30% 持续 30 分钟 | 5 分钟 | 缩容到基础数量 |
数据库不像应用层那么好弹性伸缩。实际做法:
应用层弹性伸缩 数据库层读扩展 数据库层写扩展
┌────────────┐ ┌────────────┐ ┌────────────┐
│ 应用 Pod │ │ 读库 1 │ │ 分片 1 │
│ 应用 Pod │ │ 读库 2 │ │ 分片 2 │
│ 应用 Pod ←──自动→│ 读库 N │ │ 分片 32 │
│ 应用 Pod │ │ (只读副本) │ │ (每个分片 │
│ 应用 Pod │ │ │ │ 主+从) │
└────────────┘ └────────────┘ └────────────┘
↑ K8s HPA ↑ 手动 ↑ 提前规划
弹性最快 需数分钟 需数天-周
关键认知:数据库写入层的弹性不是自动的。分库分表的扩容需要停机迁移或 Online DDL,通常在新分片上线后通过灰度流量逐步切换。这就是为什么容量规划中数据库层需要预留最大的冗余。
双 11 大促前,某支付平台的容量验证流程:
1. 预估峰值 TPS = 日常峰值 × 大促系数 × 安全系数
= 3,500 × 5 × 1.5 = 26,250 TPS
2. 分阶梯压测:
第一轮: 5,000 TPS — 验证基础链路通断
第二轮: 15,000 TPS — 验证自动扩容能力
第三轮: 26,250 TPS — 验证极限容量
第四轮(可选): 40,000 TPS — 验证熔断机制
3. 压测报告(示例):
| 压测轮次 | 目标TPS | 实际TPS | P99延迟 | 错误率 | CPU最高 | 是否熔断 |
|---------|--------|---------|---------|-------|---------|---------|
| 1 | 5,000 | 5,003 | 120ms | 0.01% | 45% | 否 |
| 2 | 15,000 | 14,890 | 280ms | 0.03% | 72% | 否 |
| 3 | 26,250 | 25,101 | 520ms | 0.12% | 89% | 否 |
| 4 | 40,000 | 24,500 | 1,200ms | 5.8% | 95% | 是(自动触发)|
结论:平台在 26,250 TPS 下基本可用(P99 < 1s,错误率 < 0.2%),40,000 TPS 时熔断机制自动触发,保护了核心系统。实际大促阈值设为 26,250 TPS,并在峰值前自动完成扩容。
传统的高可用验证方式(单元测试、手动演练)只能覆盖已知的、预期的故障模式。而真实场景中,80% 的故障是由"未知的未知"引起的——比如网络抖动与慢查询的叠加、磁盘 I/O 与垃圾回收的共振。
混沌工程的核心理念:主动注入故障,验证系统在不完美环境中的行为。
| 故障类型 | 注入方式 | 预期系统行为 | 失败表现 |
|---|---|---|---|
| 单节点宕机 | 停止 1 个 Pod | 负载均衡摘除该节点 | 流量倾斜导致级联故障 |
| 网络延迟 | 注入 100ms 延迟 | 超时重试 + 熔断 | 重试风暴击穿 DB |
| DNS 解析失败 | 污染 DNS 记录 | 切到备用 DNS | 服务完全不可用 |
| 磁盘写满 | 占满 90% 磁盘 | 触发写保护,切换到只读 | OOM / 进程崩溃 |
| CPU 打满 | 启动 CPU 密集任务 | HPA 扩容 / 降级非核心 | 请求全部超时 |
| 数据库连接耗尽 | 模拟慢查询占满连接池 | 连接池排队 → 降级 → 熔断 | 服务雪崩 |
| 外部通道超时 | Mock 通道响应超时 | 重试 3 次 → 切通道 | 重试全部失败导致无通道可用 |
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 定义假设 │───>│ 最小爆炸 │───>│ 注入故障 │───>│ 观察行为 │───>│ 修复后 │
│ │ │ 半径 │ │ │ │ │ │ 恢复 │
│ "系统在 │ │ 1%流量 │ │ 网络延迟 │ │ P99 │ │ 查根因 │
│ 网络抖 │ │ 非核心服 │ │ 100ms │ │ 1.2s → │ │ 增加超时 │
│ 动下仍 │ │ 务 │ │ │ │ 8.5s │ │ 重试策略 │
│ 能正常 │ │ │ │ │ │ (级联) │ │ │
│ 运行" │ │ │ │ │ │ │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
某支付平台在首次混沌工程演练中发现的典型问题:
| 发现问题 | 故障场景 | 影响 | 修复措施 |
|---|---|---|---|
| 重试风暴 | 1 个服务超时 → 所有调用方同时重试 | 数据库连接瞬间打满 | 指数退避 + 随机抖动 |
| 缓存雪崩 | 缓存同时过期 → 所有请求穿透到 DB | DB QPS 从 1K 飙升到 50K | 缓存 TTL 随机化 |
| 熔断级联 | 服务 A 熔断 → 服务 B 等待 A 超时 → B 也熔断 | 30% 服务不可用 | 快速失败 + 熔断传播隔离 |
| DNS 缓存穿透 | DNS 切换后本地缓存不更新 | 部分客户端持续访问已下线 IP | 降低 TTL + 客户端主动刷新 |
| 连接池泄漏 | 慢查询不释放连接 → 连接池耗尽 | 8 分钟后所有写操作失败 | 设置连接最大生命周期 + 监控泄漏 |
| 监控层 | 监控对象 | 指标类型 | 采集频率 | 告警响应 |
|---|---|---|---|---|
| 基础设施层 | CPU、内存、磁盘、网络 | 资源指标 | 10 秒 | P0: 10 分钟 |
| 中间件层 | 数据库、MQ、Redis、Nginx | 组件指标 | 10 秒 | P0: 10 分钟 |
| 应用层 | 接口延迟、吞吐、错误率 | 业务指标 | 1 分钟 | P0: 5 分钟 |
| 业务层 | 交易量、成功率、金额 | 业务指标 | 5 分钟 | P0: 5 分钟 |
| 用户体验层 | 页面加载、支付成功率 | 体验指标 | 1 分钟 | P0: 15 分钟 |
黄金信号(Google SRE 方法论):
| 信号 | 定义 | 告警阈值 | 严重级别 |
|---|---|---|---|
| 延迟(Latency) | P99 接口响应时间 | > 2s 持续 1 分钟 | P1 |
| 流量(Traffic) | 每秒请求数 | > 预估峰值的 120% | P1 |
| 错误(Errors) | HTTP 5xx + 业务错误 | > 0.5% 持续 1 分钟 | P0 |
| 饱和度(Saturation) | 资源使用率 | CPU > 85%, 内存 > 80% | P1 |
支付专用指标:
| 指标 | 定义 | 计算公式 | 告警阈值 |
|---|---|---|---|
| 成功率 | 交易成功的比例 | 成功交易 / 总交易 × 100% | < 99.5% |
| 通道可用率 | 通道正常响应的比例 | 通道成功 / 通道请求 × 100% | < 95% |
| 结算准确率 | 对账一致的比例 | 一致笔数 / 总笔数 × 100% | < 99.9% |
| 风控通过率 | 通过风控的交易比例 | 通过 / (通过+拒绝) × 100% | < 70%(异常下降) |
| MQ 积压量 | 未消费的消息数 | 生产者偏移 - 消费者偏移 | > 100,000 |
| 级别 | 定义 | 响应时间 | 通知方式 | 处理人 |
|---|---|---|---|---|
| P0 | 核心交易大面积失败 | 5 分钟 | 电话 + 短信 + 飞书高优 | 全员 On-Call |
| P1 | 功能受限,性能下降 | 15 分钟 | 飞书 @ 责任人 | 值班人 |
| P2 | 非核心功能异常 | 1 小时 | 飞书群通知 | 模块负责人 |
| P3 | 告警需关注但无需立刻处理 | T+1 | Jira 工单 | 研发团队 |
支付系统在故障期间可能产生数千条告警。需要抑制机制来避免告警疲劳:
规则 1: 同一告警源 5 分钟内只发送 1 条告警
规则 2: 如果存在 P0 告警,自动静默所有 P3 告警
规则 3: 已知故障窗口内的告警标记为"计划内"
规则 4: 根因告警优先显示,衍生告警折叠
规则 5: 告警恢复后,自动发送恢复通知并关闭工单
| 演练类型 | 范围 | 频率 | 预期 RTO | 参与方 |
|---|---|---|---|---|
| 桌面推演 | Single Service | 每周 | 无 | 值班工程师 |
| 单组件故障 | 数据库主从切换 | 每月 | < 1 分钟 | DBA 团队 |
| 单机房故障 | 同城机房切换 | 每季度 | < 5 分钟 | 全团队 |
| 异地灾备切换 | 异地机房切换 | 每半年 | < 15 分钟 | 全团队 + 管理层 |
| 极端灾难演习 | 全部机房 + 数据恢复 | 每年 | < 1 小时 | 全团队 + 监管 |
1. 演练前准备(30分钟)
├── 确认当前主从延迟 < 1s
├── 确认备份可用(最近的快照)
├── 通知相关团队"演练即将开始"
├── 暂停批处理任务
└── 记录所有监控指标基线值
2. 注入故障(1分钟)
├── 模拟主库宕机(停止主库进程)
└── 启动秒表记录 RTO
3. 自动切换(目标:< 1分钟)
├── HA 检测到主库不可用
├── 触发从库提升为主库
├── VIP 漂移到新主库
└── 验证新主库可读写
4. 验证恢复(5分钟)
├── 验证新主库数据完整性
├── 验证应用层可正常读写
├── 验证 MQ 消费者恢复消费
└── 验证对账结果一致
5. 回切(10分钟在低峰期执行)
├── 原主库修复后作为新从库加入
├── 等待数据同步追平
├── 再次执行主从切换回到原架构
└── 验证一切正常
6. 总结复盘(15分钟)
├── 记录实际 RTO 和是否达标
├── 记录发现的问题(如:DNS 缓存、连接池未重置)
├── 更新演练文档和 Runbook
└── 指定改进措施和负责人
| 演练日期 | 场景 | 预期 RTO | 实际 RTO | 预期 RPO | 实际 RPO | 问题发现 | 改进项 |
|---|---|---|---|---|---|---|---|
| 2025-01-15 | 数据库主从切换 | 60s | 42s | 0 | 0 | ✅ 通过 | 无 |
| 2025-02-20 | 单机房断电 | 300s | 215s | 0 | 12s | 部分缓存未预热 | 增加冷启动预热流程 |
| 2025-03-10 | 网络分区(脑裂) | 300s | 480s | 30s | 180s | ❌ 仲裁机制失效 | 修复 etcd 租约逻辑 |
| 2025-04-08 | 异地灾备切换 | 900s | 680s | 30s | 5s | 备库配置差异 | 统一基础设施即代码 |
支付高可用架构不是一次性的设计,而是一个持续演进的过程。关键要点: