第1章 线上故障现象分类与分级
线上故障千变万化,但绝大多数异常可以归入 10 种典型现象。本章建立统一的分类分级体系,并按照现象识别 → 数据采集 → 原因定位 → 修复动作 → 复测 → 预防六步主线,为后续章节的深度诊断打下基础。
1.1 十种故障现象总览
根据故障对业务的影响范围、响应速度、可观测性特征,将线上故障分为 响应类、资源类、性能类 三大类别。下表列出每种现象的关键指标、严重等级及初级识别手段。
| 类别 | 故障现象 | 典型指标 | 严重等级 | 初步发现手段 |
|---|---|---|---|---|
| 响应类 | 请求超时 | P99 latency > 3 s,timeout 占比 > 5% | P0 | API 监控 / Nginx 日志 |
| 响应类 | 错误率飙升 | HTTP 5xx 比例 > 10% 或异常日志激增 | P0 | 错误聚合报警 / Sentry |
| 资源类 | 内存溢出(OOM) | Java heap / native memory 持续增长,最终 OOMKill | P1 | 系统 OOM Killer 报警 / jmap 堆转储 |
| 资源类 | 线程阻塞 | 线程 dump 显示 BLOCKED 状态占比 > 30% | P1 | jstack / arthas thread 命令 |
| 性能类 | 慢 SQL | 单条 SQL 执行时间 > 1 s,慢查询日志大量输出 | P1 | MySQL slow_query_log / 数据库性能面板 |
| 性能类 | Full GC 频繁 | Young GC 频繁(< 100 ms 间隔)且 Full GC 次数/小时 > 10 | P1 | GC 日志 / jstat -gcutil |
| 资源类 | CPU 飙升 | user + system 占用 > 80%,load average > 核数 * 2 | P1 | top / htop / perf top |
| 资源类 | 磁盘 IO 打满 | await > 100 ms,%util 接近 100% | P1 | iostat -x 1 / dstat |
| 响应类 | 网络延迟 | ping RTT > 500 ms 或丢包率 > 1% | P1 | ping / traceroute / mtr |
| 资源类 | 连接池耗尽 | 连接池活跃数 == maxActive,新请求等待超时 | P1 | 应用日志 / JMX 监控 (tomcat.jdbc) |
严重等级说明:P0 指直接导致业务不可用或大规模超时,须立即响应;P1 指服务降级或局部可用性受损,须在 30 min 内介入。
1.2 故障树:从现象到根因的快速导航
故障树(Fault Tree)将顶层现象逐层分解为可量化的判断节点。以下 SVG 图展示了以“线上故障”为根节点的三分类结构,每个叶子节点对应一种故障现象,并标注了直接采集手段。
1.3 六步排查主线详解(以“CPU 飙升”为例)
本课程的所有章节均遵循同一排查主线。下面以 CPU 飙升 作为示范,串联六步操作。
步骤① 现象识别
- 指标:top 输出中 %CPU 列接近 100%,load average 显著高于 CPU 核数。
- 报警:监控系统(Prometheus + Alertmanager)触发阈值告警。
步骤② 数据采集
# 找出 CPU 消耗最高的进程和线程
top -H -p $(pgrep -f java-app) | head -20
# 将线程 ID 转为十六进制,用于 jstack 查找
printf "%x\n" <thread_id>
# 采集当前 jstack 快照
jstack <pid> > /tmp/jstack_$(date +%Y%m%d_%H%M%S).log
步骤③ 原因定位
分析 jstack 中对应线程的状态,常见原因包括:
- 死循环(持续 RUNNABLE,无 IO 等待)
- 热点正则匹配或序列化
- 频繁 GC 导致 CPU 空转(结合 GC 日志判断)
判断规则:若多次采样均发现同一个线程执行
java.lang.String.replace() 或 java.util.regex.Pattern.compile(),则大概率是代码逻辑缺陷。
步骤④ 修复动作
// 示例:优化正则缓存
private static final Pattern PATTERN = Pattern.compile("[a-z]+\\d+");
// 或者增加限流/熔断,防止突发请求打爆 CPU
步骤⑤ 复测
- 观察 top 负载是否回落至正常水平(%CPU < 60%)
- 压测或灰度环境模拟请求,确认不再触发阈值
步骤⑥ 预防
- 添加 CPU 兜底看板(Grafana 面板 + 告警规则)
- 代码审查:避免热路径上的正则与反射
- 引入 JFR(Java Flight Recorder)持续录制热点方法
1.4 排查速查表:十分钟快速定位
针对每种现象,下表列出最直接的数据采集命令和典型定位方向。使用时可将其打印为故障响应手卡。
| 故障现象 | 优先采集命令 | 定位核心方向 | 常见误判 |
|---|---|---|---|
| 请求超时 | curl -w "@format.txt" -o /dev/null -s |
依赖服务响应慢 / 连接池满 | 误认为网络抖动,却未排查下游超时配置 |
| 错误率飙升 | grep "500" access.log | awk '{print $7}' | sort | uniq -c |
代码异常 / DB 连接失败 | 忽略上游请求格式变化导致的 400 |
| 内存溢出 | jmap -histo:live <pid> | head -30 |
大对象 / 泄漏集合类 | 误判为堆外内存,其实堆内泄漏 |
| 线程阻塞 | jstack <pid> | grep -A 20 "BLOCKED" |
锁竞争 / 死锁 | 误记为 CPU 问题,实际是锁等待 |
| 慢 SQL | mysqldumpslow -t 10 /var/log/mysql/slow.log |
索引缺失 / 数据倾斜 / 锁等待 | 只分析慢日志,忽略锁等待时间 |
| Full GC 频繁 | jstat -gcutil <pid> 1000 5 |
老年代增长过快 / Metaspace 泄漏 | 盲目调大堆内存,未定位泄漏点 |
| CPU 飙升 | top -H -p <pid>; perf top -p <pid> |
热点函数 / 频繁 GC | 忽略 perf 分析,直接重启 |
| 磁盘 IO 打满 | iostat -x 1 3; pidstat -d 1 |
日志冲刷 / 临时表写入 / 抓取大文件 | 误判为网络存储故障,实际是本地日志 |
| 网络延迟 | mtr --report <target> |
丢包 / 路由跳数过多 / 防火墙 | 只测 ping 延迟,忽略 jitter |
| 连接池耗尽 | jvisualvm / JMX 获取 ConnectionPool.activeCount |
慢查询占住连接 / 未及时释放 | 误认为连接池太小,未排查 SQL 超时 |
运维原则:故障发生时,先止血(降级/限流/扩容),再采集现场数据。切勿在核心服务上直接执行耗时命令(如
jmap -dump:live 会造成停顿)。必要时使用 gcore 或 arthas 的低入侵采集方式。
1.5 本章小结
- 掌握了 10 种典型故障现象 的识别指标、严重等级分类。
- 理解 故障树 作为快速导航工具,将现象与采集手段直接关联。
- 掌握六步排查主线(现象识别 → 数据采集 → 原因定位 → 修复 → 复测 → 预防)并以 CPU 飙升为例实操。
- 获得 排查速查表,可在真实事故中 10 分钟内选择正确的采集命令与定位方向。