4. 二分法与排除法
故障排查最怕什么?最怕你面对一堆日志、一堆指标,却不知道从哪下手。我见过太多工程师,一上来就盯着最可疑的那行错误日志猛看,结果绕了一大圈才发现,根因根本不在这。
说白了,排查故障就像在黑暗里找一只漏水的管子。你不可能把整栋楼的水管都拆开检查。你需要的是——快速缩小范围。而二分法和排除法,就是帮你做到这一点的两把手术刀。
核心思想:不要试图一次定位到根因。先确定「问题在哪一半」,再确定「问题不在哪一块」。每次操作都把嫌疑范围缩小一半或排除一块,几次之后,真凶自然浮出水面。
4.1 二分法:从中间切一刀
二分法的逻辑很简单。你有一个有序的排查路径,比如时间线、请求链路、代码调用栈。你从中间切一刀,看问题出现在前半段还是后半段。然后继续切,直到锁定。
我个人习惯在以下场景优先使用二分法:
- 时间维度:故障从什么时候开始的?把时间轴一分为二,看前半段正常、后半段异常,还是反过来。
- 请求链路维度:一个请求经过 A → B → C → D。你在 B 和 C 之间加个检查点,看问题出现在 B 之前还是之后。
- 数据范围维度:全量数据有问题,还是只有某一部分数据有问题?把数据按 ID 范围或时间范围切半。
我的经验:有一次线上支付接口超时率飙升。我没有逐台服务器看日志,而是直接看网关的请求分布。发现超时集中在 14:00-14:05 这个时间段。然后我把这 5 分钟按秒切半,发现 14:02:30 之后超时率骤降。最终定位到是 14:02 发布的一个配置变更导致的。整个过程不到 10 分钟。
二分法的前提是:你的排查路径必须是可比较的。你不能拿「上午的 CPU 使用率」和「下午的磁盘 IO」去比,那没有意义。你要确保每次对比的变量只有一个。
4.2 排除法:逐个拔掉嫌疑
排除法更像是「不是 A,不是 B,那就是 C」的逻辑。你列出一堆可能的根因,然后一个一个排除掉。每排除一个,嫌疑范围就缩小一圈。
排除法适合以下场景:
- 多组件依赖:系统依赖了数据库、缓存、消息队列、外部 API。你不知道是哪个挂了,那就逐个隔离测试。
- 多版本共存:线上跑了多个服务版本,怀疑某个版本有 bug。你把该版本的流量切走,看问题是否消失。
- 环境差异:开发环境正常,预发环境正常,线上环境挂了。排除代码问题,剩下的就是配置或数据问题。
| 方法 | 适用场景 | 操作方式 | 典型耗时 |
|---|---|---|---|
| 二分法 | 时间线、请求链路、有序数据 | 从中间切一刀,对比前后 | 3-5 次操作可定位 |
| 排除法 | 多组件、多版本、环境差异 | 逐个隔离或回滚 | 取决于嫌疑项数量 |
避坑指南:我曾经犯过一个错误——用排除法时没有记录已经排除过的项。结果排查到一半,忘了之前测过哪个组件,又重复测了一遍。浪费时间不说,还差点误判。所以,我建议你每次排除一个嫌疑,就在文档或群里打一个勾。保持排查过程可追溯。
4.3 两者结合:先二分,再排除
实际排查中,我很少只用一种方法。通常是先二分,把大范围缩小到一个小范围,然后再用排除法,在小范围内逐个验证。
举个例子:
- 第一步(二分法):发现所有用户都报错,还是只有部分用户?如果是部分用户,按用户 ID 范围切半,看是前 50% 还是后 50%。
- 第二步(二分法):锁定到后 50% 的用户后,再看这些用户的请求集中在哪些机房或集群。按机房切半。
- 第三步(排除法):锁定到某个机房后,列出该机房的所有依赖组件(数据库、缓存、配置中心)。逐个隔离测试,排除正常的组件。
- 第四步:找到那个异常的组件,定位根因。
你看,整个过程就像剥洋葱。一层一层剥开,每次只处理一小块。不会让你一开始就面对整个系统的复杂度。
4.4 实战中的常见陷阱
方法再好,用不对也是白搭。我总结几个常见的坑,你遇到了可以绕开走。
- 陷阱一:二分点选错了。比如按时间切半,但你选的切分点恰好是故障的「波动期」,前后都有问题。这时候二分法会失效。我建议你多选几个切分点交叉验证。
- 陷阱二:排除法排错了顺序。先排除了最容易验证的项,结果发现真正的根因是那个最难验证的。浪费时间。我建议你先排除「影响面最大」或「最容易回滚」的项。
- 陷阱三:忽略了「无中生有」。有时候问题不是某个组件坏了,而是多了一个不该有的东西。比如某个定时任务被重复调度,或者某个配置项被意外覆盖。排除法只能排除「存在」的嫌疑,但排除不了「不存在」的嫌疑。这时候你需要结合变更历史来看。
我的习惯:每次开始排查前,我会先在脑子里过一遍:「这个问题能用二分法吗?能用排除法吗?还是两者结合?」如果 30 秒内想不出答案,我就直接看最近的变更记录。因为 80% 的故障都是变更引起的。先看变更,往往比盲目二分更高效。
4.5 小结
二分法和排除法不是高深的理论,它们是你在故障现场最实用的工具。记住几个要点:
- 二分法适合有序的排查路径,每次切半,快速缩小范围。
- 排除法适合多嫌疑场景,逐个隔离,直到剩下最后一个。
- 两者结合使用,先二分再排除,效率最高。
- 每次操作都要记录,避免重复排查。
- 别忘了先看变更记录——那可能是最快的捷径。
嗯,下次你遇到故障,别急着翻日志。先问自己一句:「我能从中间切一刀吗?」