Java

接口响应慢怎么排查?后端 API 变慢与超时的定位步骤

接口一慢,最浪费时间的做法就是所有人同时冲向不同技术点。先看影响范围,再分清是执行慢还是等待慢,排查链才不会一开始就散。

  • Java
  • 性能排查
  • 接口优化
  • 后端开发
  • 线程池
18 分钟阅读

接口 RT 抬高、超时增多、业务方开始催,这些现象经常会在同一时间冒出来;但真把现场拆开看,拖慢请求的那一段未必是同一个地方。

有时候起点是某条 SQL 退化了,有时候是长事务把连接和锁一起拿住了,也有时候数据库、CPU、GC 都不算高,真正的问题却是隐藏等待点、下游依赖或线程排队。

所以接口慢排查最怕的,不是不会看监控,而是还没分清问题属于哪一类,就先冲进某个技术点里深挖

如果你只记一条主线,我更建议记住这句:

先看影响面,再拆调用链;先区分执行慢还是等待慢,再决定继续往数据库、下游依赖、并发资源还是 JVM 方向走。

下面只把第一轮排查顺序拎清:先看影响面,再拆调用链,再判断是执行慢还是等待慢。等你把问题压到某一类,再去查单接口、数据库等待链或隐藏等待,会快很多。

如果你现在还没分清是单接口慢还是整个服务都在慢,可以先看 单接口变慢和整个服务变慢,排查入口为什么完全不同?。如果你已经看到数据库没打满,但 API 和连接池已经开始变慢,就直接去看 数据库没打满,为什么 API 和连接池已经开始变慢?

接口已经开始变慢时,先别急着钻技术细节

很多排查效率低,不是因为少了命令,而是第一步没有先做分诊。你至少先回答下面这几件事。

先看什么这更像哪类问题下一步先去哪里
只有一个接口慢,其他接口基本正常更像局部链路、参数、SQL、事务或独有下游问题单接口变慢和整个服务变慢,排查入口为什么完全不同?
多个接口一起慢,甚至整组服务都在抬 RT更像共享资源、共享数据库、连接池、线程池或公共下游问题先继续看本文,后面按信号转数据库等待链或隐藏等待
平均 RT 还行,但 P95 / P99 已经明显抬高更像尾延迟、等待链、高峰期坏路径问题P99 抖动高但平均 RT 正常,应该怎么定位?
CPU、GC、数据库都不高,但接口还是慢更像隐藏等待、下游依赖、线程排队、连接池或锁等待接口慢但 CPU、GC、数据库都不高,常见隐藏等待点有哪些?
获取连接时间先变差,随后接口 RT 一起抬高更像数据库等待链、长事务、锁等待、连接池传导数据库没打满,为什么 API 和连接池已经开始变慢?
只有部分实例慢,或发布后才开始慢更像实例差异、配置漂移、局部状态异常先做实例对照,再决定回单接口链还是共享链路

这一步的目的只有一个:先决定从哪条链起步,不要一上来就猜根因。

二、第一步先看影响面:你面对的是单链路问题,还是共享问题

接口慢的第一步,不是先抓线程栈,也不是先改参数,而是先判断影响面。

至少先回答这四个问题:

  • 是单接口慢,还是多个接口一起慢
  • 是单实例慢,还是整组实例都慢
  • 是平均 RT 一起变差,还是主要是尾延迟在抬高
  • 是发布后开始慢,还是高峰期放大,还是全天持续变慢

为什么这一层最重要?因为它会直接改变排查顺序。

1. 单接口慢

更常见的方向通常是:

  • 这条接口独有的 SQL 或事务边界有问题
  • 某类参数、某批数据、某个租户触发了坏路径
  • 这条链路独有的 RPC / HTTP 依赖变慢
  • 热点 key、热点行、本地锁竞争只集中在这条业务线上

这种场景下,不要一上来就把整个数据库、整个 JVM、整个网络都翻一遍。先回到这条接口自己的调用链,通常更快。

2. 多个接口一起慢

更应该优先怀疑:

  • 共享数据库或共享下游依赖一起变慢
  • 连接池、线程池、网关、缓存等公共资源紧张
  • 某类等待链已经扩散到多个接口
  • JVM、宿主机、网络、发布变更等系统性因素在放大

这种场景下,先看共享层,再看代表性接口,效率会高很多。

3. 只有部分实例慢

这更像第三类问题,不完全属于“单接口慢”,也不是真正的“整站慢”。

常见方向有:

  • 灰度或最近发布后的配置漂移
  • 个别实例上的线程池、连接池、缓存状态异常
  • 流量倾斜、定时任务、机器资源问题只打在少数实例上

如果你看到的是这种现象,别直接跳去数据库深处,先做实例对照更值钱。

三、第二步再拆调用链:时间到底花在了哪一段

“接口慢”只是表象。你真正需要的是把时间压缩到某一段明确的链路上。

一个典型请求通常会经过:

  • 网关或入口层
  • 业务逻辑与参数处理
  • 缓存、数据库读写
  • RPC / HTTP / MQ / 搜索等下游依赖
  • 对象组装、序列化、返回

所以更关键的问题是:

时间主要花在本服务内部,还是花在外部依赖上?是执行慢,还是等待慢?

1. 有链路追踪时,先看三个点

优先看:

  • 哪个 span 最长
  • 数据库耗时和下游耗时谁更突出
  • 问题是集中在单个调用点,还是整条链都在抖

2. 没有完整 trace 时,也尽量补齐这几段时间

至少尽量拿到:

  • 获取连接耗时
  • SQL 执行耗时
  • RPC / HTTP 调用耗时
  • 业务计算耗时
  • 请求排队或线程等待耗时

这一步做完,你才能把“接口慢”进一步压缩成下面几类更可操作的问题:

  • 数据库链路慢
  • 下游依赖慢
  • 应用执行慢
  • 应用等待慢

四、第三步先分清:你看到的是执行慢,还是等待慢

很多团队看到 RT 变高,会本能地去查 CPU 或代码热点。但大量接口慢问题,本质其实不是“算得慢”,而是“等得久”。

更像执行慢的信号

  • CPU 持续高,且热点线程明确在做业务计算
  • 某个方法、某段对象转换、序列化、规则计算明显耗时
  • 单线程执行路径本身就长,没有明显排队感

更像等待慢的信号

  • CPU 不算高,但 RT 和超时一起变差
  • 获取连接时间、锁等待、线程排队时间在变长
  • 某个下游调用不一定很多,但一慢就把整条请求拖住
  • 写接口、事务接口或高峰期更容易放大问题

如果你已经更像第二类,就不要把主要精力先花在代码微优化上。等待型问题通常更该沿着数据库、下游依赖、线程池、连接池这条线继续收敛。

五、接口慢时,优先沿这四条主分支继续收敛

到这一步,你不需要一次把所有方向都查完,只需要选一条最像当前现象的分支继续往下走。

1. 数据库等待链:慢 SQL、锁等待、长事务、连接池

如果你看到这些信号:

  • 获取连接时间先变差
  • 写接口、事务接口更容易变慢
  • 数据库没打满,但应用已经开始排队
  • 锁等待、事务耗时、连接持有时间明显拉长

那就别只盯慢 SQL,把数据库链路放回一条完整等待链里看。下一步更适合去:

2. 隐藏等待与下游依赖:资源图不高,但请求还是慢

如果 CPU、GC、数据库都不算高,但接口 RT 还是明显抬升,很多时候真正的问题在:

  • RPC / HTTP 下游等待
  • 队列积压
  • 线程池排队
  • 本地锁或分布式锁等待
  • 缓存回源、热点 key、异常重试

这类场景更适合去 接口慢但 CPU、GC、数据库都不高,常见隐藏等待点有哪些?

3. 并发资源:线程池、请求排队、共享资源被占满

如果接口慢的核心感觉是“还没轮到执行”,常见信号往往是:

  • 线程池活跃线程接近上限
  • 队列长度持续上涨
  • 吞吐下降,但 CPU 不一定同步拉满
  • 请求等待时间比执行时间更夸张

这时先看线程池、连接池、共享资源是否已经被前面的慢请求拖住,比先看某个业务方法更有效。

4. CPU / GC:执行热点或停顿放大器

如果你看到:

  • CPU 明显升高
  • GC 时间和 RT 抖动强相关
  • 某些实例频繁停顿或吞吐明显掉下去

再继续往 JVM 方向查才更划算。建议分别接:

六、接口慢里最容易误判的地方

误判 1:先猜数据库,再找证据去证明

数据库当然是高频方向,但如果你连影响面、调用链、等待位置都没分清,先猜数据库只会把排查变成碰运气。

误判 2:只看平均 RT,不看 P95 / P99

很多故障在早期只表现为尾延迟上升,平均值还没明显变坏。只看平均值,很容易把故障初期信号漏掉。

误判 3:CPU 不高,就以为不是性能问题

很多接口慢是等待问题,不是计算问题。CPU 不高,反而经常说明线程在等数据库、等锁、等下游、等连接。

误判 4:把所有数据库问题都当成慢 SQL

慢 SQL、锁等待、长事务、连接池等待,是四类完全不同的问题。只盯 SQL,很容易错过真正的等待源。

误判 5:一看到发布后变慢,就只查代码 diff

发布后确实可能引入问题,但它也可能只是改变了参数分布、流量路径、事务边界或实例状态。发布只是时间线线索,不等于根因本身。

七、FAQ:接口响应慢时最常被问到的几个问题

1. 接口慢时,应该先看日志还是先看监控?

先看监控和调用链,先把问题缩到某一段,再回到日志和代码。日志更适合补证据,不适合当第一分诊工具。

2. 接口慢但 CPU 正常,是不是就不用看 JVM 了?

不一定,但优先级通常要往后放。CPU 正常更常见的是等待型问题,先看数据库、下游依赖、线程排队和连接池,会更接近根因。

3. 接口超时和接口变慢,是不是同一类问题?

通常属于同一条链上的不同阶段。接口变慢如果继续放大,就会进入超时;所以排查顺序基本一致,都是先分影响面,再拆调用链,再看等待源。

4. 数据库没打满,但接口和连接池已经开始变慢,还要不要继续查数据库?

要,至少要先做一次判断。数据库不高负载不代表数据库等待链安全,锁等待、事务持有、连接占用都可能在资源图不夸张时就先把应用拖慢。

八、如果现场更像下面这些情况

如果第一轮分诊已经做完,但你发现问题更贴近下面某一种现象,就直接切到对应那篇:

九、把主线收一下

遇到接口变慢、接口超时、后端响应慢时,不要一上来就猜根因。更稳的顺序是:先分影响面,再拆调用链;先判断是执行慢还是等待慢,再决定往数据库、下游依赖、并发资源还是 JVM 方向继续走。

只要第一步分诊没错,大多数接口慢问题都会从“哪里都像原因”,收敛成“这一段最值得先查”。