生产故障排查总纲:先止血再定位
线上故障和实验室里的性能调优是两件事:系统正在流血、用户正在受损,任何“边查边改”都会搅乱现场,让后面的定位失去依据。本章给出排查的总原则——先恢复可用性再找根因,先看变更再看指标,先摘流量再动现场。
止血优先于定位
排查的第一个动作不是打开终端敲命令,而是判断“能不能先让用户好起来”。可用性每多损失一分钟的代价,都远高于根因晚知道十分钟。
| 手段 | 适用场景 | 生效速度 | 代价与前提 |
|---|---|---|---|
| 回滚 | 故障紧跟发布、配置、DDL 变更 | 分钟级 | 丢失新功能,必须先确认变更是可逆的 |
| 摘流量 | 单实例或单机房异常,多实例部署 | 秒级 | 容量下降,必须先确认剩余容量扛得住 |
| 降级 | 依赖(DB、缓存、三方接口)故障 | 秒级到分钟级 | 非核心功能不可用,需产品提前约定 |
| 扩容 | 容量不足、流量真实增长 | 分钟级 | 成本上升,且掩盖不了代码缺陷 |
选择标准只有两条:能不能在 1 分钟内生效、能不能回退。做不到这两点的“救火方案”,本身就是新的风险。
止血动作的推荐顺序
故障发生
├─ 最近 30 分钟内有发布/配置/DDL 变更? → 有:立即回滚,回滚之后再来查
├─ 只有部分实例异常? → 是:先从负载均衡摘除异常实例,保留现场
├─ 核心依赖超时或报错? → 是:降级非核心链路,只保登录/下单/支付
└─ 流量真实超出容量? → 是:扩容 + 限流,按业务优先级放行
顺序反了代价很高:一边扩容一边等根因,流量持续压在坏实例上,用户照样报错,而扩容还把费用花掉了。
黄金四指标
任何线上系统,先看这四个信号就够了,其余指标都是它们的下钻。
| 指标 | 含义 | 观察形式 | 异常时的第一反应 |
|---|---|---|---|
| 延迟 Latency | 请求处理耗时 | P95/P99,不要看均值 | 区分是自身慢还是依赖慢 |
| 流量 Traffic | QPS/TPS、并发连接数 | 按接口、按实例拆开看 | 是真涨了还是重试放大 |
| 错误 Errors | 错误率、超时率 | 按状态码与异常类型分 | 先看错误类型,不看错误总数 |
| 饱和度 Saturation | CPU/内存/连接池/队列水位 | 百分比与排队长度 | 找最先逼近 100% 的那一项 |
平均延迟是故障排查中最没用的数字之一:9 个请求 10ms、1 个请求 10s,均值只有 1s,而投诉已经来了。
三分钟内要回答的六个问题
- 谁受影响:哪些用户、哪些接口、哪个地域或机房?
- 什么时候开始:精确到分钟,与最近一次变更是前是后?
- 范围多大:全量故障,还是单实例、单机房、单租户?
- 趋势如何:正在恶化、保持持平,还是已经自愈?
- 有没有变更:发布、配置、开关、DDL、数据订正、证书、依赖方升级?
- 有没有止血手段:能否回滚、摘流量、降级、扩容?
这六个问题答不上来就动手,大概率会把故障范围扩大一倍。
定位的推荐路径
用户反馈/告警 → 看大盘(延迟 + 错误率 + QPS)→ 确认影响面与开始时间
↓
比对变更记录(发布、配置、依赖、数据)
↓
应用层:日志关键字 + TraceID 全链路
↓
主机层:CPU / 内存 / 磁盘 / 网络 / 句柄
↓
确认根因 → 临时补丁 → 长期修复 → 复盘归档
路径是从上到下,不是从下到上。主机层是最后一站:多数“CPU 飙高”只是上游流量或自身代码问题在主机上的投影。
典型误操作
| 误操作 | 为什么错 | 正确做法 |
|---|---|---|
| 一上来就重启 | 现场没了,根因无从查起,问题大概率复发 | 先摘流量保住现场,再决定是否重启 |
| 一边查一边改配置 | 指标基线被自己改掉,前后数据不可比 | 一次只改一个变量,改前记录基线 |
| 忘了看变更 | 多数故障与变更相关,白查一小时 | 第一件事查发布与配置变更记录 |
| 只看均值不看分位 | P99 已经爆炸,大盘看起来依然“正常” | 看 P95/P99 与分接口错误率 |
| 直接连生产库查大表 | 二次故障,慢查询把库打挂 | 走只读从库、加 limit、避开业务高峰 |
| 群里只说“在查了” | 信息不同步,重复排查、决策冲突 | 固定节奏同步影响面、已做动作、结论 |
重启前必须留下的现场
- 线程栈:连续采 3 次,间隔 5 秒,用来看线程是否卡在同一处。
- 火焰图:CPU 型故障采 30 秒,通常足够定位热点函数。
- 堆快照:内存型故障先 dump 再重启,否则泄漏证据丢失。
- 日志与指标:重启会清空本地日志,先转存到别处。
- 连接与句柄快照:
ss -s、lsof -p <pid> | wc -l。
# 重启前的现场保存清单,按顺序执行
ss -s > /tmp/snap_ss.txt 2>&1
lsof -p <pid> | wc -l >> /tmp/snap_ss.txt
top -Hp <pid> -b -n 1 > /tmp/snap_threads.txt
kill -3 <pid> # JVM 打印线程栈到应用日志
dmesg -T | tail -200 > /tmp/snap_dmesg.txt
预防与巡检
- 每次变更都有回滚方案,并且回滚方案被演练过,不是纸面存在。
- 核心接口的告警阈值按 P99 设置,而不是按平均值设置。
- 容量水位常态化巡检:CPU、内存、磁盘、inode、连接数、句柄数。
- 故障处置流程写在文档里并定期演练,避免凌晨三点靠人回忆。
复盘:把故障变成资产
故障恢复不等于结束。复盘只回答四个问题:什么现象、什么根因、为什么没被提前发现、下次怎么更快恢复。不追责,但必须落到具体的监控项、代码改动或流程修改上,否则同样的故障一年内一定重来。
小结:故障现场的第一优先级是恢复可用性,回滚、摘流量、降级、扩容四选一或组合,动作必须一分钟内生效且可回退;定位按变更、指标、日志、链路、主机的顺序自上而下推进,重启前先保存线程栈与火焰图,最后用复盘把根因变成长期监控与代码改动。