慢接口排查思路
慢接口排查思路(口述版)
面试口述版(优先背这个)
一个接口变慢,我一般不会先改代码,而是按「先定位、再分层、后优化」来排查。
第一步:先确认慢在哪。 先分清是前端慢、网关慢,还是后端接口本身慢。看接口总耗时、QPS、错误率,对比是个别用户慢,还是全量慢;是偶发,还是持续。如果有链路追踪(SkyWalking、Pinpoint、Zipkin),先看整条调用链,哪一段耗时最长,基本就能圈定范围。
第二步:按调用链路一层层拆。 一个请求通常经过:网关 → 应用服务 → 缓存 / 数据库 / 下游 RPC / 消息队列。我会把耗时拆开:
- 网关到应用的网络和排队时间
- 应用内部业务逻辑时间
- 数据库 SQL 时间
- Redis 等缓存时间
- 下游接口时间
哪一层占比最大,就先查哪一层。
第三步:应用层常见问题。 看线程池是否打满、有没有锁等待、有没有同步调用外部接口、有没有一次性加载太多数据、有没有循环里查库(N+1)。必要时抓线程栈(jstack)看线程都卡在什么方法上。
第四步:数据库往往是重灾区。 打开慢 SQL 日志,对可疑 SQL 做 EXPLAIN。重点看有没有全表扫描、有没有走错索引、有没有深分页、有没有大事务、有没有锁等待。很多慢接口最后都是一条慢 SQL 或一次回表过多。
第五步:缓存和下游。 看 Redis 是否命中率低、是否有大 Key、是否发生了缓存穿透/击穿。下游接口如果慢,要看超时设置、是否可以异步、是否需要降级或批量调用。
第六步:确认优化效果。 改完后对比优化前后的 RT、P99、错误率,避免只优化了平均值、长尾依然很慢。
一句话总结:
先用监控和链路追踪定位慢在哪一层,再针对应用、数据库、缓存、下游分别处理,最后用数据验证。
详细排查清单
1. 先定性:慢到什么程度、谁在慢
| 问题 | 目的 |
|---|---|
| 是页面慢还是接口慢? | 排除前端渲染、静态资源 |
| 是某一个接口慢,还是一批接口都慢? | 判断是单点问题还是整体负载 |
| 是偶发还是持续?高峰才慢? | 判断容量、锁、GC、慢 SQL |
| P99 / P999 还是平均 RT 高? | 平均低、长尾高,往往是个别慢 SQL 或外部依赖 |
| 是否伴随超时、500、线程池拒绝? | 判断是否已经打满 |
常用工具:Nginx / 网关日志、Prometheus、Grafana、APM、SkyWalking。
2. 按链路拆耗时
客户端
→ 网关 / Nginx
→ 应用服务器(Controller / Service)
→ Redis
→ MySQL
→ 下游 HTTP / RPC
→ 消息队列
每一段都要能回答:这段花了多少毫秒。
- 网关慢:连接排队、限流、鉴权、带宽
- 应用慢:业务逻辑、序列化、锁、线程池排队、GC
- DB 慢:SQL、索引、锁、连接池不够
- 缓存慢:网络、大 Key、热 Key、穿透
- 下游慢:对方 RT 高、超时设置不合理、同步串行调用
3. 应用层怎么查
重点看这几件事:
- 线程池有没有满
Tomcat / 业务线程池队列堆积,请求在排队,接口就会变慢。 - 有没有同步堵在外部调用
一个接口里串行调 3、4 个下游,每个 200ms,加起来就接近 1 秒。 - 有没有循环查库 / 循环调接口
典型 N+1:查 100 条订单,再循环查 100 次用户。 - 有没有大对象序列化、大结果集一次性返回
- GC 是否频繁
Full GC 时接口会集体卡顿。 - 锁竞争
synchronized / 分布式锁粒度太大,线程都在等锁。
常用手段:
- 看应用日志里的方法耗时
jstack看线程卡在哪- Arthas
trace/watch定位热点方法 - 看 GC 日志、线程池监控
4. 数据库怎么查(最常见)
排查顺序:
- 慢查询日志找出耗时 SQL
EXPLAIN看执行计划- 看扫描行数、是否走索引、是否 filesort / temporary
- 看有没有锁等待、死锁、长事务
- 看连接池是否耗尽(Druid / Hikari 监控)
常见原因和对应优化:
| 原因 | 优化 |
|---|---|
| 没索引 / 索引失效 | 加索引,避免对索引列套函数 |
深分页 limit 100000,10 |
延迟关联或按上一页最大 ID 翻页 |
SELECT * 回表多 |
只查需要字段,覆盖索引 |
| 一次性查太多 | 分页、限制返回条数 |
| 大事务 / 锁等待 | 缩短事务,避免在事务里调远程 |
| 连接池不够 | 调大连接池,同时优化 SQL 减少占用时间 |
5. 缓存和下游怎么查
缓存:
- 命中率是否下降
- 是否大 Key、热 Key
- 是否穿透、击穿、雪崩
- Redis 本身是否慢(bigkey、CPU、网络)
下游:
- 下游 RT 本身高
- 没有超时,把线程拖死
- 可以并行的却串行调用
- 可以异步 / 消息队列的却同步等待
优化方向:超时 + 重试(有限次)+ 降级 + 并行(CompletableFuture)+ 批量接口。
6. 一个可直接说的排查顺序(面试很好用)
- 看监控:接口 RT、QPS、错误率、P99
- 看链路追踪:定位最慢的 span
- 如果是 SQL:慢日志 + EXPLAIN
- 如果是应用:线程栈 + 方法耗时 + 线程池
- 如果是下游:看对方 RT、超时、是否可异步
- 如果是缓存:命中率、大 Key、穿透
- 优化后对比 P99,确认长尾也降下来
项目里可以这样举例
「有一次列表接口 P99 到了 2 秒。链路追踪发现 80% 时间在一条订单查询 SQL 上。EXPLAIN 显示没走索引,原因是对时间字段用了函数,索引失效。加上合适索引,并把循环查用户改成一次 IN 查询后,P99 降到 200ms 以内。」
这种「现象 → 定位工具 → 根因 → 优化结果」的叙述最加分。
一句话收尾
慢接口排查不是上来改代码,而是:
监控确认 → 链路拆分 → 定位最慢一层 → 针对优化 → 用 P99 验证。