Skip to content

接口变慢了应该如何排查?导致接口变慢的原因有哪些? ​

  • 标签:后端、场景题

🏛 业务架构师 ​

接口变慢是运维事故里最高频的一类,我的处理框架是先定影响面,再定位,最后根治——顺序不能乱。

第一步:业务影响面确认(很多工程师跳过这步)

  • 慢的是核心交易接口还是边缘功能?影响多少用户、持续多久、转化率掉了多少?
  • 这决定了是"立刻回滚止损"还是"慢慢排查"。大促期间核心下单接口 P99 涨 200ms 就是 P1 事故,管理后台慢 2 秒可能只值一张低优先级工单。资源投放跟着业务损失走。

第二步:链路定位(分段二分法)

  • 看全链路追踪(Trace)确定慢在哪一段:网关 → 应用 → 下游 RPC → 数据库。
  • 应用内再看:GC 日志(Full GC/长停顿)、线程池堆积、锁竞争(jstack 看 BLOCKED)。
  • 数据库层:慢 SQL 日志、执行计划(索引失效、锁等待、深分页)。
  • 常见根因排序(我的实际经验):慢 SQL(约一半)> 下游依赖抖动 > 连接池/线程池打满 > GC > 序列化/大报文。

第三步:止损与根治分开

  • 止血手段:回滚最近变更(70% 的变慢是发布引入的)、降级非核心依赖、扩容。
  • 根治:加索引/改写 SQL、热点数据加缓存、串行调用并行化、非核心链路异步化(MQ)、超时与熔断兜底。

架构师视角的收尾:单次排查之外,更重要的是建立机制——给核心接口定 P99 SLO 并告警,容量水位常态化巡检,变更必须可回滚。慢接口从来不是突然慢的,是监控缺位让它"突然被发现"。

🏗 系统架构师 ​

接口变慢我按"先定位、再归因"两步走,最忌讳上来就猜是数据库。

第一步:全链路定位慢在哪一环 前提是有链路追踪(OpenTelemetry/SkyWalking),拿 traceID 看 span 瀑布图:网关 → 服务 → DB/Redis/下游 RPC,耗时卡点一眼可见。没有 trace 就退化为分层看监控:入口 P99、应用内部耗时、依赖组件耗时三者做差。再配合 arthas trace 类 方法 -n 5 在方法内逐层下钻,定位到具体哪一行。

第二步:按归因分类,我实践中占比从高到低

  • 慢 SQL(最常见):索引失效、深分页、大事务。查慢日志,explain 看执行计划。
  • 锁与线程竞争:连接池耗尽(获取连接排队几百 ms)、synchronized 热点、线程池队列堆积。看连接池 active 水位和线程 dump。
  • GC 停顿:Young GC 频繁或 Full GC 秒级停顿,GC 日志确认,通常是缓存对象过大或内存泄漏。
  • 下游拖累:第三方接口抖动且没设合理超时,调用方线程被拖死,进而雪崩。重试风暴会放大 2~3 倍流量。
  • 量变引起质变:数据量涨了(单表从 500 万到 3000 万,B+ 树多一层)、流量翻倍触发排队论里的延迟非线性上升。

解决思路对应四板斧 加缓存挡读、异步化削非关键路径、并行化串行调用、扩容 + 限流保命。最后我会补一句:优化前先立 P99 基线和监控报警,否则无法证明优化效果——这是架构师和业务开发最大的区别。

💻 全栈开发专家 ​

接口变慢我先按「定位环节 → 定位原因 → 修复」三步走,不瞎猜。

第一步:定位慢在哪个环节(1 分钟内)

  • 有 APM(SkyWalking)直接看这条接口的 span 拓扑:慢在网关、应用、DB、Redis 还是下游 RPC。
  • 没有 APM 就看分层监控:网关 RT vs 应用 RT vs DB 慢查询计数,谁先涨谁有问题。

第二步:应用内定位(arthas 是利器)

bash
trace com.xxx.OrderService createOrder -n 5 '#cost > 200'

一条命令打出方法调用树和每层耗时,哪一行慢一目了然。同时 watch 看入参、dashboard 看 GC。

常见原因我按概率排:

  1. 慢 SQL:数据量涨了索引失效(隐式类型转换、函数包字段、区分度差的联合索引顺序)。EXPLAIN 确认,改索引或改写 SQL。
  2. 锁等待:synchronized 热点、数据库行锁冲突。jstack 看 BLOCKED 线程。
  3. GC 停顿:老年代频繁 Full GC。GC 日志确认,调堆或换 G1/ZGC。
  4. 下游变慢:第三方接口/_rpc 超时配置过长,拖长整体 RT。查依赖方监控。
  5. 线程池/连接池排队:池耗尽,请求在队列里等。看 HikariPool 指标 ConnectionTimeout 报错。
  6. 大对象序列化 / 日志同步刷盘 / 深分页 这类代码层问题。

修复优先级:能加缓存的加缓存(Redis/本地),能异步的异步(MQ/@Async),SQL 加索引,最后才是重构代码。

我踩过的坑:一次接口变慢查了两小时代码,最后发现是同机房一台实例磁盘坏了,IO wait 高——所以先看机器指标(CPU/IO/网络)再看代码,能少走弯路。