3312 字
约 11 分钟
19
S19-X13 · 问题拆解与假设驱动的排查思维

线上报错,第一反应常是"把日志级别调高再重启看看"。这个动作不坏,但它不是排查,是碰运气。排查的本质是在信息不全时用最小代价缩小可能性空间——每做一次动作,都要能回答"这次排除了哪些可能"。

下面按执行顺序拆:定现象 → 列假设 → 排优先级 → 单变量验证 → 二分收敛 → 确认机制 → 留证。


1. 排查的本质:缩小可能性空间

一个故障的可能原因空间是有限的(几十到几百个候选),排查就是不断切分这个空间。两种做法在动作上很像,在收敛速度上差很远:

维度 凭感觉试 假设驱动
动作来源 记忆里"上次是这个" 从机制推"哪一环"
每次动作的信息量 常为 0(改了也不知排除了啥) 明确排除一个分支
变量控制 同时改几处 一次只改一处
收敛方式 随机游走 每轮至少减半
结束后 不知为什么好了 能写出机制与复发条件

**差别在"每次动作后能排除什么"。**答不上来,这个动作就只是在消耗时间。

自检:**你能说出三个可能原因吗?各自被什么证据区分?**只能说一个,说明你在验证猜测,不是排查。


2. 第一步:把现象说清楚,而不是猜原因

现象不清,后面的推理都建在流沙上。先固定四件事:

要素 要问的问题 为什么必须先定
可复现性 必现 / 偶发 / 只在某环境 决定能否做实验验证
影响面 哪些用户、接口、多少比例 定优先级,也缩小范围
时间边界 从何时开始,与某事件是否同时 指向"最近变更"
最近变更 部署、配置、依赖、数据、证书、流量 先查这一项命中率高

写成一句话的现象陈述,比"系统很慢"有用得多:

现象陈述模板
- 现象:<接口> 在 <条件> 下 <表现>,错误原文:<粘贴>
- 范围:影响 <用户/接口>,占比 <n/总数>,其他接口是否正常
- 时间:首现 <时间>,是否持续,是否与 <发布/配置/流量> 同时
- 复现:<环境> 下按 <步骤> 可复现 <n/m> 次
- 已排除:<已验证不成立的假设及证据>

最后一行最容易被跳过,也最能防重复劳动:没有它,三个人会把同样的三个假设各验一遍。


3. 假设从机制来,不从记忆来

从记忆里捞,只能捞到见过的;从机制里推,才能覆盖没见过的。做法是把请求链路画成线,逐个节点问"这里坏了会有什么表现":

机制树(沿请求路径逐层展开)
入口 → DNS/CDN → 网关 → 认证 → 线程池 → 缓存 → DB/下游 → 磁盘/网络
每层问三个问题:
  a. 这一层失败时现象是什么?(报错 / 超时 / 错数据 / 静默变慢)
  b. 可观测证据有哪些?(指标 / 日志 / 链路 / 系统命令)
  c. 最快的区分动作是什么?

展开后按层的性质归类,可以快速定位"现象与层的对应关系":

层 典型现象 首选证据
网络/DNS 连接超时、解析失败、间歇失败 curl -v、dig、建连耗时
网关/限流 429、502、504,只影响部分请求 网关日志、限流计数
线程池/队列 延迟升高但 CPU 不高 线程栈、队列长度、park 占比
缓存 命中率骤降、DB QPS 突增 命中率、慢查询数
数据库 慢查询、锁等待、连接打满 慢日志、show processlist
资源 OOM、磁盘满、句柄耗尽 内存/磁盘/inode/ulimit

**列假设先求覆盖,不求正确。**典型情况:列 5–8 个候选,其中 3 个能用一条命令当场否掉,剩下 2–3 个才需要实验。


4. 验证的优先级:三类假设先做

不是所有假设都值得验证。三条依据,命中任意一条就提前:

依据 含义 例子
一击排除 一个观测否掉一整片可能 看连接数,同时排除"池满"与"下游挂"
代价最低 一条命令 vs 一次发布 先 curl,后改代码
覆盖最广 一个证据服务多个假设 时间线对齐一次,验"最近变更"全部子项

排序后的执行顺序大致是:

第 1 轮(分钟级,只读)
  □ 时间线:现象起点 vs 发布/配置/证书/流量变更点是否重合
  □ 范围:单接口还是全站?单实例还是全集群?
  □ 资源:CPU/内存/磁盘/inode/连接数/fd 是否触顶
  □ 错误原文:日志里第一条异常,而非最后一条

第 2 轮(十几分钟,轻量实验)
  □ 复现:可控环境复现一次,确认不是看错了
  □ 二分:范围对半切(实例/时间/参数/数据)

第 3 轮(需要改动)
  □ 单变量变更 + 明确预期 + 明确回滚

第 1 轮刻意全用只读动作:不引入新变量、不污染证据;任何一次重启或改配置都会把现场洗掉。


5. 一次只改一个变量

同时改三处然后好了,信息量为零:不知哪一处生效,也不知另两处是否在掩盖问题。紧急时最易破这条纪律,代价也最大。

动作 可归因 后续代价
改 A,观察,记录 是 多花一轮时间
同时改 A/B/C 否 复发无法定位,可能引入新问题
改 A 回滚再改 B 是 需要能快速回滚
重启+改配置+扩容 否,且破坏现场 证据丢失,复盘无据

紧急时允许"同时改以止损",但守两条:**记下同时改了哪几处;止损后逐个回滚复现。**止损是恢复服务,归因是另一件事。

变更记录(每次改动一行)
时间 | 改动 | 预期 | 实际 | 回滚
10:02 | 线程池 200→400 | 队列下降 | 仍涨 | 是
10:15 | 关掉新上线的 N+1 查询 | 慢查询归零 | 归零,延迟恢复 | 否

这张表写起来三十秒,却直接决定复盘时能不能说出根因。


6. 二分法:适用范围比想象中窄

二分的前提是有"可判定的分界",且两边是同一维度的两段。五种最常用:

维度 怎么切 判定信号 注意
版本 上一版 / 当前版 切回后现象是否消失 数据库变更常不可回退
配置 一半实例用旧配置 是否只在旧配置实例上 需要能定向路由
流量 按用户/租户/IP 两组现象差异 两组条件要相近
数据 按大小/编码/地区 只有某类数据出错 要能定位具体样本
时间 按时间窗口 起点前后行为差异 注意缓存与预热

不适用三种:原因是单点且确定(证书过期看日期即可);切分会破坏现场(只能重启验证);两侧不同质(灰度实例规格本就不同)。切之前先确认两组在其他维度可比,否则比的是两个混杂变量。


7. "改了就好了"不等于找到了原因

现象消失有两种可能:原因被移除,或原因还在但触发条件暂不满足。区分只看一条:能否用机制解释"为什么这个改动会让现象消失"。

状态 判据 风险
已定位 能说出机制链 A→B→C→现象 低
已规避 现象消失但说不清机制 高,会复发
假性恢复 因流量降了或重启清空状态 很高,下次更难查
归因错误 有解释但与证据冲突 高,会改错地方

典型情况:重启后恢复,结论写成"内存泄漏,已重启解决"。但重启同时清空了连接池、缓存、队列、临时文件。证据不足时正确写法是"现象随重启消失,机制未确认"。

判据:**解释能否预测一个尚未观察到的现象?**若你说"连接未释放导致耗尽",就该能观察到连接数单调上升并在重启前触顶。


8. 排查日志:把过程变成可复用的资产

排查结束后能留下的只有记录。价值不在存档,在两点:复发不用重来,别人能直接用。

## 排查记录 YYYY-MM-DD HH:MM

### 现象
<一句话现象 + 错误原文>

### 假设与验证
| # | 假设 | 验证手段 | 结果 | 结论 |
|---|---|---|---|---|
| 1 | 下游超时致线程堆积 | 下游 P99 + 线程栈 | P99 正常,栈多在 DB 等待 | 排除 |
| 2 | 慢查询打满连接池 | 慢日志 + 连接数 | 连接数打满 | 成立 |
| 3 | 缓存命中率下降 | 命中率指标 | 平稳 | 排除 |

### 结论
机制链:<查询未走索引> → <单次 800ms> → <连接池占满> → <排队> → <超时>

### 复发判据(下次先看这三处)
1. 连接池 active 与 wait 同时升高
2. 慢日志条数与延迟同步上升
3. 缓存命中率不变(用于区分"缓存失效")

### 未解决
<现在还解释不了的部分>

表格式的"假设→验证→结论"是核心:后来者能直接看到哪些已排除。


9. 排错:五种排查本身失效的情况

现象 原因 处理
查很久没收敛 假设都来自同一层 重画链路,从没查过的层列假设
每次结论都不同 同时改多处,证据被污染 回滚到已知状态,单变量重来
复现不了 依赖环境/数据/时序 用流量或数据二分,不强求本地复现
修好两周后复发 只做规避,未确认机制 补"复发判据",观测项进告警
三人查出三结论 没共享"已排除"清单 建共享记录,先对齐已排除项

第三条展开:**"不能复现"不是终点,而是要求换验证手段。**偶发问题多源于竞争或时序,靠单机复现很难,但可用"哪类请求出错、哪类不出错"来二分。


10. 边界:什么情况不该先排查

场景 为什么 先做什么
正影响大量用户 排查耗时不可接受 止损(回滚/降级/扩容)再排查
现场正被破坏(磁盘满) 证据会丢 先保全证据:导出日志、抓快照
原因是明确单点事实 排查是浪费 直接核对证书日期、配置值、版本号
需跨团队协作 单人会卡在权限 先拉人对齐"已排除"清单
无可观测性 没有证据可依 先补最小观测,再谈定位

取舍一句话:止损优先于归因,证据保全优先于操作。


11. 自测

  1. 排查的本质是什么?凭什么判断一次动作有没有价值?
  2. 现象陈述里"已排除"这一栏为什么必须写?
  3. 假设应该从哪里推出来?为什么不从记忆里捞?
  4. 验证优先级的三条依据分别是什么?为什么第 1 轮要全是只读动作?
  5. "重启后恢复正常"为什么不能直接写成"已定位"?正确的写法是什么?
  6. 二分的三种不适用情况是什么?
S19-X13 · 问题拆解与假设驱动的排查思维
https://www.clxhxhhr.top/posts/5896/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。