第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 图展示了以“线上故障”为根节点的三分类结构,每个叶子节点对应一种故障现象,并标注了直接采集手段。

线上故障分类故障树 线上故障 响应类故障 资源类故障 性能类故障 请求超时 错误率飙升 网络延迟 内存溢出 线程阻塞 CPU 飙升 磁盘 IO 打满 连接池耗尽 慢 SQL Full GC 频繁 🛠 直接采集命令对应表(叶子节点可快速启动): 请求超时 → curl -w "%{time_total}" ; 错误率飙升 → tail -f access.log | awk '$9~/(5[0-9]{2})/' 内存溢出 → jmap -heap <pid> ; 线程阻塞 → jstack <pid> ; CPU 飙升 → top -H -p <pid> 磁盘 IO → iostat -x 1 ; 网络延迟 → mtr --report ; 慢 SQL → mysqldumpslow ; Full GC → jstat -gcutil <pid> 1000

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 分钟内选择正确的采集命令与定位方向。