第2章:故障排查方法论

故障排查这事儿,说白了就是跟系统斗智斗勇。你面对一个出问题了的系统,信息杂乱,症状千奇百怪。如果不掌握一套系统性的思考方法,很容易陷入「修好这里,那里又崩」的恶性循环。

我在刚做运维的头两年,最怕半夜接到报警电话。一着急就乱试,重启大法、重装大法轮着来,十次里有五次能把问题搞得更复杂。后来慢慢总结出几套通用的方法论——它们不是万能公式,但能让你在慌乱中快速找到切入点。

这一章,我们把这四种方法单独拿出来掰开揉碎地讲。别指望一口吃成胖子,每理解一种,你就多一把手术刀。

核心观点:方法不是教条,是思维框架。熟练后,你会下意识地组合使用它们。

故障排查方法论知识图谱 故障排查方法论知识图谱 故障排查方法论 排除法 二分法 因果分析法 5W2H归纳 逐步缩小怀疑范围 适用:模块独立、可隔离 复杂度低 常见于硬件/网络故障 对半切分排查范围 对数级减少检查量 适用:线性链路、长流程 常用于网络/日志链路 追溯因果关系链条 鱼骨图+5Why 适用:复杂问题、根因深 常见于性能/代码逻辑 结构化归纳信息 What/Why/Who/Where/ When/How/How much 复盘总结、建知识库 四种方法可组合使用:排除法快速缩小范围 → 二分法定位到模块 → 因果分析法深挖根因 → 5W2H归纳复盘沉淀

2.1 排除法

排除法是最基础、最直觉的故障排查方法。我习惯叫它「俄罗斯方块消消乐」——把可能的嫌疑模块一个个拿掉,剩下的自然就是问题所在。

核心逻辑:列出所有可能原因→逐个假设→通过验证排除→留下唯一解释。

我在排查一个数据库连接超时的问题时用过它。当时有应用A、B、C三个服务都连同一个MySQL实例。A正常,B和C都报超时。按排除法:先检查网络ping通→OK,排除网络层。再检查端口telnet→A和B能通,C不通。好,问题就锁定在C到MySQL之间的防火墙或路由。最后发现C的Docker网络策略更新后没放行3306端口。

  1. 列出所有可能的故障原因(少则三五个,多则十几个)
  2. 按可能性从高到低排序(别一上来就查最冷门的)
  3. 对每个原因做验证——验证一个排一个
  4. 如果所有原因都被排除,重新审视问题定义

排除了所有不可能的,剩下的即使再难以置信,那也是真相。——柯南·道尔(但福尔摩斯的名言也适用运维,虽然真相往往更「普通」)

适用场景:模块间耦合度低、可独立启停、可单独测试的环境。比如多实例集群、微服务架构(但微服务调用链长时排除法效率反而低,后面会讲二分法)。

避坑:我曾经犯过一个错,排除了10个可能原因后,发现第1个原因其实没完全排除干净——因为验证条件不充分。所以,每次排除时一定要记录「我是用什么方法、什么数据排除的」,否则白忙活。

2.2 二分法

二分法的思路很简单:把排查范围对半切,判断故障在哪一半,然后递归。你想想看,如果一条请求链路上有10个节点,从中间节点检查,一次排除一半,最多log₂(10)≈4次就能定位。而用排除法从头查到尾,运气差可能要10次。

我记得有一次排查用户登录卡慢问题。前端→Nginx→网关→认证服务→用户中心→数据库,六跳链路。如果从第一个节点慢慢查,代码翻起来很费劲。我直接用二分法:从网关打一个模拟登录请求,发现网关响应正常,说明问题在后半段(认证服务→用户中心→数据库)。再在认证服务出口加日志,发现认证服务查用户中心接口耗时1.2秒,怀疑数据库查询慢。进一步二分割,在用户中心DAO层加慢查询日志,确认是SQL没走索引。用了4步就锁定。

  1. 梳理出端到端的链路节点(物理层、网络层、应用层都行)
  2. 在中间位置插入检查点——打印、监控、tcpdump都行
  3. 判断正常/异常,取包含异常的那一半
  4. 重复直到定位到单一节点

二分法的前提是你能在两个半区之间清晰区分「正常」与「异常」。如果看不到中间状态,比如全链路只靠一个最终错误返回,那就没法二分——这时候先得增加观测点。

适用场景:长链路、多层调用、批处理流程。网络问题的经典套路:「从客户端到服务端中间加ping/traceroute,看丢包在哪一跳出现」。这正是二分的变体。

避坑:千万不要在切分点不清楚的环节强行二分。我见过有人在我心链路里,从第5个节点往两边拆,但两边都没办法独立验证,结果白折腾。一定要切在「能单独做返回判断、能独立观测」的位置。

2.3 因果分析法

因果分析法不是用来定位故障点,而是用来回答「为什么会导致这次故障」——说白了,找到根因(Root Cause)。常见工具有鱼骨图(石川图)和5Why分析法。

我刚到第二家公司时,团队经常出「硬件故障」甩锅的问题。后来我们组推行5Why,我带头试了一次:某数据库主库宕机。

  • Why1:数据库进程OOM被kill → 内存不足
  • Why2:某个慢SQL导致缓存击穿,瞬间大量请求打到磁盘?→ 慢SQL未优化
  • Why3:该SQL来自凌晨的报表任务,任务占用了超量内存 → 报表任务未限制并发数
  • Why4:DBA没有给该任务设置资源限制 → 没有资源池管控
  • Why5:没有对报表任务做压测和容量评估 → 运维流程缺失预发布验证

你看,表面是OOM,根因是流程问题。如果只重启数据库,下个月可能还会炸。

因果分析的关键原则:

  • 不止于表面症状——问至少5个Why,直到「人/流程/系统」的层面
  • 区分直接原因和根本原因——直接原因是「数据库内存被耗尽」,根本原因可能是「没有SLA约束」
  • 不要假设自己已经知道答案——每次Why都可以是开放式问题

5Why不是万能药。我见过有人追到第3个Why就开始编答案:「因为……因为那天是周五,大家都想下班。」——没有数据支持的Why就是瞎猜。每个Why必须基于可查证的日志、监控、变更记录。

2.4 5W2H归纳总结

故障修完了,不代表结束了。如果不做归纳总结,下次同样的故障换个马甲,你还是认不出来。5W2H是一套结构化归纳模板,适合写故障复盘报告、建知识库。

维度 含义 示例(某次缓存雪崩)
What 发生了什么故障?现象? 15:20 开始,首页接口超时率从0.1%飙升到35%
Why 直接原因/根本原因 Redis集群主从切换,大量请求打到数据库致雪崩
Who 谁影响的(团队/系统/变更人) 运维团队在10:00做了一次Redis扩缩容操作
Where 影响范围(业务模块/机器/区域) 所有依赖首页BFF的业务线,约300台机器
When 发生时间/持续时间/恢复时间 15:20~15:45,持续25分钟
How 怎么排查的?一步步过程 先用二分发现缓存不可用→检查Redis集群状态→定位到主从切换→回滚扩容操作
How much 量级评估(损失/耗时/资源消耗) 影响约10万用户,预估损失1000元(若计入品牌影响更高)

我个人习惯在每次故障复盘后,用这套模板写一份《故障复盘小结》,挂在团队Wiki里。下次再遇到类似问题,直接搜索「What」关键词就能找到。在我不在组里的时候,新人也能快速回溯。

5W2H的「How much」很容易被忽略。但在我负责的一个电商项目里,每次故障都会估算损失金额(哪怕0元),连续几个月后,有些开发不再随意申请变更——因为「变更可能值几百块」这个数字比安全会议更有冲击力。

小技巧:5W2H不是写作文,每条写一两句话就够了。核心是「可复现、可追溯、可行动」。我见过有人写「Why:代码有问题」——这种等于没写。必须具体到「为什么代码有问题?是哪个分支、哪次合并引入的?对应的commit sha是什么?」

2.5 如何组合使用这四种方法

你发现在实际排查中,我并不是死板地只用一种方法。一般套路是:

  • 接到报警,先用排除法快速扫一眼常见的、已知的高频原因(比如磁盘满、CPU飙高、端口不通)。花5分钟如果没头绪,立刻切换。
  • 对长链路,上二分法,在中间插埋点。这个阶段通常伴随tcpdump、trace、thread dump、数据库慢查询等工具。
  • 锁定大致范围后,用因果分析法深挖。这时候5Why能帮你把看似「偶发性」的问题变成「可预防」的根因。
  • 最终定论、写复盘时,用5W2H归纳总结。

四种方法不是对立的,而是从「发现」到「定位」到「理解」到「归档」的全流程武器库。

我这么多年最大的教训就是:别在排查过程中跳步骤。比如定位到某个模块后,不搞清楚根因就着急修复——结果一个月后又炸了。因果分析法和5W2H归纳,是负责的工程师和普通背锅侠的分水岭。