Java面试要点:接口变慢、链路追踪、慢SQL与性能瓶颈定位
前言
接口变慢是最常见的线上故障之一。面试官通常不会只问“怎么优化 SQL”,而是会追问:用户反馈接口慢,你怎么判断慢在哪里?如何证明是数据库慢、缓存慢、线程池满、网络慢,还是下游服务慢?生产上怎么止血?
典型现象
- 单个接口 RT 从 200ms 上升到 3s 以上。
- P99 延迟升高,但平均耗时变化不明显。
- 只有部分用户、部分租户、部分区域慢。
- 应用 CPU 不高,但请求大量超时。
- 数据库慢查询增加,或者连接池等待时间变长。
排查接口慢不能只看一次请求,要同时看平均值、P95、P99、错误率、吞吐量和依赖调用耗时。
排查总链路
flowchart TD
A[用户反馈接口慢] --> B[确认范围]
B --> C[查看网关和APM]
C --> D[拆分接口耗时]
D --> E[应用内部逻辑]
D --> F[数据库]
D --> G[缓存]
D --> H[MQ或第三方接口]
E --> I[线程池/锁/CPU]
F --> J[执行计划/索引/锁等待]
G --> K[热Key/大Key/网络抖动]
H --> L[超时/重试/熔断]
J --> M[止血与长期优化]
K --> M
L --> M
第一步:确认慢的范围
先回答几个问题:
- 是所有接口慢,还是某个接口慢?
- 是所有实例慢,还是某台机器慢?
- 是所有用户慢,还是某类参数慢?
- 是刚发布后慢,还是流量突然升高后慢?
- 是读接口慢,还是写接口慢?
范围判断决定排查方向。所有接口都慢,优先看机器资源、网关、线程池、数据库连接池、下游公共依赖。单接口慢,优先看代码逻辑、SQL、缓存和参数分布。
第二步:用链路追踪拆耗时
生产环境通常会接入 SkyWalking、Pinpoint、Zipkin、Jaeger、CAT 或 OpenTelemetry。看链路时重点关注:
- 网关耗时。
- Controller 到 Service 的业务耗时。
- SQL 执行耗时。
- Redis 调用耗时。
- HTTP/RPC 下游调用耗时。
- 重试次数和异常分支。
如果链路显示 SQL 只用了 30ms,但接口耗时 3s,就不能把问题甩给数据库。可能是应用内循环调用、远程接口慢、线程池排队、锁等待或序列化耗时。
第三步:检查应用日志
接口日志建议带上:
- traceId。
- userId 或 tenantId。
- 请求参数摘要。
- 各阶段耗时。
- 异常码和下游错误码。
生产中不要只打印入口和出口日志。复杂接口要记录关键阶段耗时,比如参数校验、权限校验、查主表、查明细、查缓存、调用第三方、组装返回值。
常见日志结论:
- 日志入口很快,出口很慢:应用内部或下游慢。
- 入口日志都很少:请求可能卡在网关、负载均衡或连接层。
- 同一 traceId 多次重试:重试策略可能放大故障。
- 某类参数特别慢:索引选择、数据倾斜或业务分支异常。
第四步:慢 SQL 定位
数据库方向重点看:
- 慢查询日志。
- SQL 执行计划。
- 索引命中情况。
- 返回行数和扫描行数。
- 锁等待。
- 临时表和文件排序。
- 分页是否过深。
MySQL 常用排查点:
1 | EXPLAIN SELECT * FROM order_info WHERE user_id = ? ORDER BY create_time DESC LIMIT 20; |
执行计划重点看 type、key、rows、Extra。如果出现全表扫描、Using filesort、Using temporary,就要结合数据量判断是否需要联合索引、覆盖索引或改写 SQL。
常见慢 SQL 根因
| 根因 | 表现 | 处理方式 |
|---|---|---|
| 索引缺失 | 扫描行数大 | 增加合适索引 |
| 索引失效 | 有索引但未命中 | 避免函数、隐式转换、前置模糊匹配 |
| 分页过深 | limit offset 很大 | 游标分页、搜索后翻页 |
| 返回字段过多 | 网络和反序列化慢 | 只查必要字段 |
| 大事务 | 锁等待和 undo 增长 | 拆小事务 |
| 数据倾斜 | 少数用户数据极多 | 分库分表、冷热分离、异步汇总 |
第五步:检查缓存
缓存慢不一定是 Redis 本身慢,也可能是应用访问模式有问题:
- 缓存未命中率突然升高。
- 热 Key 被集中访问。
- 大 Key 读写导致网络和序列化耗时。
- 缓存穿透导致数据库压力上升。
- Redis 连接池耗尽。
如果接口依赖多个 Redis Key,要注意是否存在循环调用。例如列表 100 条数据,每条都查一次 Redis 和一次 DB,这种 N+1 调用在压测低流量时不明显,线上数据量上来后会放大。
第六步:检查线程池和连接池
很多接口变慢不是业务逻辑慢,而是在排队。
需要关注:
- Tomcat 工作线程是否打满。
- 业务线程池队列是否堆积。
- 数据库连接池 active、idle、wait。
- Redis 连接池等待时间。
- HTTP 客户端连接池是否耗尽。
典型链路是:慢 SQL 增多 -> DB 连接被长期占用 -> 请求线程阻塞等待连接 -> Tomcat 线程打满 -> 所有接口变慢。
第七步:第三方依赖和重试
支付、短信、物流、文件服务等第三方接口经常是接口慢的根因。排查时要看:
- 调用超时时间是否过长。
- 是否同步调用了非核心依赖。
- 是否有多次重试。
- 是否缺少熔断和降级。
- 是否把第三方失败拖进主事务。
生产上,非核心能力尽量异步化。比如下单成功后的短信通知、站内信、积分发放,可以放到 MQ 消费,不要阻塞主链路。
止血方案
- 打开降级开关,关闭非核心查询或第三方调用。
- 临时扩容应用实例和数据库只读实例。
- 对慢接口增加限流,保护核心链路。
- 对热点数据增加本地缓存或预计算。
- 回滚最近发布版本。
- 针对慢 SQL 加临时索引,但要评估建索引对线上写入的影响。
长期优化
- 接口分层打点,沉淀统一耗时日志。
- 接入链路追踪和指标监控。
- 建立慢 SQL 审核机制。
- 核心接口做压测和容量基线。
- 对高频查询做缓存、预聚合或读模型。
- 对第三方依赖设置合理超时、隔离和熔断。
高频面试题
用户说接口慢,你第一步做什么?
先确认范围和影响面:哪个接口、哪个时间段、哪些用户、哪些实例、错误率和 P99 是否升高。然后通过链路追踪拆分耗时,定位慢在网关、应用、数据库、缓存还是下游。
慢 SQL 怎么排查?
先看慢查询日志和执行计划,再看索引命中、扫描行数、排序方式、锁等待和返回数据量。不能只凭 SQL 文本判断,要结合表数据量、参数分布和执行计划。
接口慢但数据库不慢怎么办?
继续看应用内部耗时、线程池排队、锁竞争、Redis 调用、第三方接口、序列化和网络。数据库不慢只说明一个依赖没问题,不能说明接口本身没问题。
总结
接口变慢排查的核心是拆链路、看数据、定范围。面试回答要体现生产思维:先止血,再定位,再修复,最后沉淀监控和容量基线。


