线上问题排查

DEBUG FIELD NOTES

如何快速定位"能跑但不稳定"的线上问题

"偶发错误"最消耗时间。真正有效的排查,不是先改代码,而是先把问题描述清楚:什么情况下发生、多久发生一次、用户影响面多大。

01 / 症状归类

先分清是功能错误、性能抖动,还是数据一致性问题

"不稳定"是一个太模糊的描述,收到反馈后第一件事不是打开日志翻,而是先分类。不同类型的问题排查路径完全不同:

  1. 功能错误:返回值明显不对,通常能从输入条件切入。比如"传参 A 就报错"或"特定用户看不到数据"。这类问题最容易定位——找到特定输入,复现就行。
  2. 性能抖动:高峰期变慢、偶尔超时,重点看依赖与资源竞争。CPU 飙高?数据库连接池满了?下游接口 P99 抖了?挨个排查,先看资源指标再看业务指标。
  3. 数据一致性:同一请求前后结果不一致,优先检查缓存与并发更新。这类问题最隐蔽,因为你看到的数据"看起来是对的",只是偶尔不对。典型场景:缓存过期瞬间的脏读、并发写入导致的部分更新。

分清类型之后,排查方向会窄很多。最怕的是"不分类型乱翻日志"——翻了两小时还没找到线索,因为一开始方向就错了。

02 / 最小复现

把复杂现场缩成 3~5 个稳定步骤

能不能稳定复现,基本决定了排查效率的上限。我一般会先写一份"复现脚本草稿",包含以下信息:

  1. 请求参数——精确到 header 级别,不要漏掉 content-type 和认证信息。
  2. 调用顺序——哪个接口先调,哪个后调,中间有没有并发请求。很多时候问题出在时序依赖上。
  3. 预期结果 vs 实际结果——别只记"报错了",要记"预期返回 200 实际返回 500,错误信息是 connection timeout"。
  4. 触发频率——每次必现?十次出一次?高峰期才有?频率本身就是一个重要线索。

只要复现稳定,后面的排查效率会快很多。如果复现不了,先别急着改代码——花时间在"制造复现条件"上,比盲改有效得多。我见过太多人在复现不了的情况下凭猜测改代码,改了上线发现问题还在,白白浪费一个迭代周期。

03 / 观察与定位

日志要能回答"发生了什么",而不是"代码走到了哪里"

很多团队的日志问题是:记了很多行,但没有一条能回答关键问题。有效的日志应该像时间线叙事——读完就知道发生了什么、在哪一步出了问题:

  1. 每次关键调用都记录 request id、关键参数摘要、耗时与结果码。没有 request id 的日志基本没法串起来看,你只能靠时间戳猜哪些日志属于同一次请求。
  2. 异常日志要有上下文:调用链位置、依赖状态、重试次数。只记一个 Exception: null 等于没记——你还得回去加日志、重新发布、等它再复现一遍。
  3. 如果有分布式链路(比如 OpenTelemetry、Jaeger),先对齐时间戳和时区。我遇到过两个服务一个用 UTC 一个用本地时间,排查了一下午发现是时区不一致导致"日志顺序对不上",问题本身其实十分钟就能修。

04 / 修复与验证

修复不是终点,验证方案才是

很多人找到原因后立刻改代码上线,结果过了几天问题又复现了——因为只修了表面,没修根因。比如某个接口偶发超时,你加了重试确实缓解了,但根因是下游连接池配置太小,不修连接池,问题迟早还会以别的形式冒出来。

补丁上线前,至少准备三组验证:正常路径(原有功能不受影响)、异常路径(触发条件被正确拦截或降级)、边界输入(极端参数不会导致新问题)。上线后观察一段时间(通常 24~48 小时),确认问题不再复现,再关闭问题单。过早关单是"线上问题反复出现"的常见原因之一。

← 返回首页