故障排查思维:从现象到根因的推理路径
做运维这些年,我见过太多人在故障面前手忙脚乱。系统无响应了,第一反应就是重启——这其实是最偷懒的做法。重启确实能解决问题,但你永远不知道根因是什么,下次它还会来找你。
今天我想聊聊,当系统无响应时,我们该怎么思考。说白了,就是一套从现象到根因的推理方法。
现象不等于根因
先讲个我自己的经历。有一次线上数据库突然响应超时,监控报警响成一片。团队里有人立刻说:「数据库挂了,快重启!」
我拦住了他。为什么?因为「数据库无响应」只是现象,不是根因。重启后可能暂时恢复,但过半小时又会出现同样的问题。
我习惯先问自己三个问题:
- 这个现象是什么时候开始的?——时间点很关键
- 变化了什么?——代码发布?配置变更?流量突增?
- 影响范围有多大?——全部节点还是个别实例?
你想想看,如果连这些都不清楚,就直接动手操作,那跟蒙着眼睛修车有什么区别?
5Why分析法:追问到底
5Why分析法,是丰田生产体系里常用的根因分析工具。名字叫5Why,但实际问几次不重要,重要的是「追问到底」。
举个例子,系统无响应:
- 为什么系统无响应?——因为应用进程CPU使用率100%
- 为什么CPU使用率100%?——因为某个线程在死循环
- 为什么会出现死循环?——因为某个while循环的条件永远为真
- 为什么条件永远为真?——因为缓存中的状态值没有被更新
- 为什么状态值没有被更新?——因为并发写入时出现了竞态条件,更新操作被覆盖了
你看,到第5个Why,根因才浮出水面。如果只问到第2层,你可能会选择「杀掉进程重启」,但真正的根因是代码里的并发bug。
假设驱动法:先猜后验证
5Why是事后分析的好工具,但故障发生时,我们往往等不及一步步追问。这时候,我更喜欢用假设驱动法。
说白了就是:先根据现象提出几个可能的根因假设,然后快速验证,排除错误选项。
具体怎么做?我一般分四步:
- 收集信息——看监控、查日志、问当事人
- 提出假设——列出3-5个最可能的根因
- 设计验证——每个假设对应一个验证方法
- 执行排除——从最可能的开始验证,直到找到根因
举个例子,系统无响应,我可能会提出这些假设:
| 假设 | 验证方法 | 预期结果 |
|---|---|---|
| 数据库连接池耗尽 | 查看数据库连接数监控 | 连接数达到上限 |
| 磁盘IO瓶颈 | 执行 iostat 查看磁盘等待时间 | await > 100ms |
| 内存不足触发OOM | 查看系统日志 dmesg | 出现 Out of memory 信息 |
| 死锁导致线程阻塞 | jstack 查看线程堆栈 | 发现 BLOCKED 状态的线程 |
嗯,这里要注意:假设不是瞎猜。每个假设都要有依据,比如之前遇到过类似问题,或者监控数据有异常信号。
避坑指南:我曾经犯过一个错误——只验证了最明显的假设,就匆忙下结论。那次系统无响应,我一看CPU高就认定是死循环,结果折腾了半天才发现是磁盘IO满了。从那以后,我要求自己至少验证3个假设,才能开始修复。
两种方法怎么配合?
你可能会问:5Why和假设驱动法,到底用哪个?
我的建议是:
- 故障发生时——用假设驱动法。快速定位,先恢复服务再说。
- 故障复盘时——用5Why法。深挖根因,避免重复发生。
说白了,一个是救火,一个是防火。两者缺一不可。
我记得有一次线上事故,用假设驱动法10分钟就定位到是缓存雪崩,快速恢复了服务。但复盘时用5Why一追问,才发现是缓存预热策略有问题,导致大量请求直接打到数据库。如果不做这一步,下次换个场景还会出事。
总结一下
故障排查,说白了就是一场推理游戏。现象是线索,根因是真相。5Why帮你深挖,假设驱动帮你快找。
我个人习惯在工位上贴一张便签,上面写着:
「现象 ≠ 根因,先问三个为什么,再列三个假设。」
每次故障来了,看一眼,思路就清晰了。