真实场景 · 排查流程 · 命令速查 · 思维模型 · 手把手排查
不管什么问题,都走这个流程。养成习惯之后,排查速度会快 3~5 倍。
Debug 就像医生看病:先问症状(复现)→ 做检查(缩小范围)→ 初步诊断(假设)→ 开药(修复)→ 复查(验证)。不要上来就改代码猜答案!
碰到复杂问题,用二分法快速缩小范围:
如果是后端 → 服务层还是数据库层?(看接口RT)
如果是数据库 → 慢查询还是锁等待?(看慢查询日志)
如果是服务层 → 代码逻辑还是外部依赖?(看日志+链路追踪)
经验之谈:80% 的线上问题能在 10 分钟内定位,关键是问对问题。"是网络问题吗?是代码问题吗?是数据问题吗?"——每次只排除一半。
找到 ERROR 日志后,不要只看第一行!要看完整的异常链(Caused by):
一个请求可能经过多个服务,用 traceId 串联:
没有 traceId?看请求的入参(userId、orderId)一样可以串起来。
把多条日志按时间排列,看先发生了什么:
很多时候你看到的错误只是表象,要往上游找:
不要在"下单失败"这一层修 bug,要追到根因!
• NullPointerException → 查空值来源,判空处理
• IllegalArgumentException → 参数校验问题,看入参
• SQLException → SQL 语法/连接池满/死锁
• TimeoutException → 下游服务/数据库/Redis 超时
• OutOfMemoryError → 内存问题(见 OOM 章节)
• 检查入参:前端传的参数对不对?(看 Network 面板)
• 检查逻辑:代码有没有处理边界情况(null、空字符串、0)
• 检查数据:数据库的数据是不是预期的?(直接 SQL 查)
• 检查缓存:是不是读了旧缓存?(清缓存试试)
紧急处理:线上 OOM 或 CPU 100% 时,先导出 dump 再重启!重启后现场就没了。用 jmap -dump 先把堆存下来,然后重启恢复服务,再慢慢分析 dump 文件。
遇到以下情况,第一反应要想到"可能是并发问题":
• 偶发性 Bug:本地永远复现不了,线上偶尔出现
• 数据不一致:两个请求同时操作,结果不对
• 重复执行:用户双击提交,订单创建了两个
• 计数不准:并发下 i++ 结果偏小
• 死锁:线程卡住不动,CPU 接近 0
现象:用户快速点两次"提交",创建了两个订单。
原因:前端没防重复提交 + 后端没做幂等。
解决方案:
• 前端:提交按钮加 loading,防双击
• 后端:用 Token 机制或唯一索引(orderId + userId 联合唯一)
• 后端:用 Redis setnx 做防重(key = "order:"+userId+":"+hash,TTL=5s)
现象:库存 10 件,卖了 12 件。
原因:读库存和扣库存之间没有加锁。
解决方案(从简单到复杂):
① 数据库乐观锁:UPDATE stock SET count=count-1 WHERE id=1 AND count>0
② Redis 原子操作:DECR key(扣到 0 以下就拒绝)
③ 分布式锁:Redisson lock + 扣库存
④ 消息队列:库存扣减请求串行化
| 问题 | 现象 | 排查命令 | 常见原因 |
|---|---|---|---|
| Redis 慢 | GET/SET 耗时高 | SLOWLOG GET 10 | BigKey、复杂命令、内存满 |
| 连接超时 | Connection refused | redis-cli INFO clients | maxclients 达上限、网络问题 |
| 内存满 | 写入报 OOM | INFO memory | 没设过期、BigKey、淘汰策略不对 |
| 数据丢失 | 重启后数据没了 | CONFIG GET save | RDB 没保存 / AOF 未开启 |
| 缓存雪崩 | 大量请求打到DB | 看DB连接数飙升 | 大量 key 同时过期 |
确认报警真实性(是误报还是真问题)
确认影响范围(影响多少用户?哪个功能?)
看最近的部署/变更记录("刚才发版了吗?")
看应用 ERROR 日志(最近的异常)
看监控大盘(CPU/内存/QPS/RT)
看 HTTP 状态码(4xx 参数问题 / 5xx 服务端问题)
看 Response Body(有没有具体错误信息)
看应用日志(grep 请求ID或关键词)
看上游/下游服务日志(是不是依赖方的问题)
看数据库/Redis(是不是存储层的问题)
看网络(ping / telnet 测试连通性)
确认是"一直慢"还是"偶尔慢"
看接口 RT 分布(P50/P95/P99)
看慢查询日志
看 GC 日志(是不是 Full GC 停顿)
看线程池状态(是不是线程池满了排队)
看 Redis 响应时间
看网络延迟(ping / traceroute)
最重要的一条经验:遇到问题先别急着改代码!花 5 分钟完整描述问题(什么时间、什么操作、什么现象、什么环境),80% 的 bug 在描述的过程中就能猜到原因。