记一次线上接口超时排查:从日志到GC再到慢SQL的全过程 📅 2026/8/26 1:36:57 上周三下午同事在群里甩了一张监控截图某个订单查询接口P99延迟突然从80ms飙到3s偶尔还直接504。当时第一反应是——谁又上了什么骚操作。先看日志别瞎猜直接去ELK捞日志按traceId串了一下调用链发现大部分耗时都卡在一个查订单列表的RPC调用上。这个接口平时QPS不高大概200左右平时响应很稳突然炸了就很反常。// 大致逻辑简化版 ListOrder orders orderMapper.selectByUserId(userId);SQL本身不复杂就是按user_id查加了索引理论上不该慢。看监控排除网络抖动先看了下网络层面的监控丢包率正常TCP重传也没异常。然后看下游数据库的连接池状态HikariCP的active连接数已经打满了wait线程一堆。到这里基本可以确认——不是网络问题是数据库那边扛不住了。慢SQL日志里捞线索去数据库侧看了下慢查询日志果然有一堆类似的SQL执行时间从1s到5s不等。但诡异的是这条SQL在测试环境跑得飞快explain出来也走的索引type是refkey_len也正常。这时候有个同事提了一嘴是不是数据量变了去线上库一查那个user_id对应的订单数据量大概有12万条而且没有分页直接全量select。SELECT * FROM t_order WHERE user_id 10086;12万条没limit全量拉回来。之前这个用户是个普通用户数据量小所以一直没暴露。这次是个大客户的测试账号数据量一上来就炸了。根因说白了就是代码里没做分页默认全量查测试数据量太小没覆盖到大用户场景连接池被打满后其他正常请求也被拖死了怎么修的改动不大主要就两件事接口加上分页参数默认page_size20最大不超过100对全量查询的场景做了兜底超过阈值直接拒绝并告警if (count MAX_ALLOWED) { log.warn(user_id{} order count{} exceed limit, userId, count); throw new BizException(数据量过大请使用筛选条件); }另外顺手把select *改成了指定字段减少网络传输量。几个教训测试数据要贴近真实分布不能全是几条数据的理想场景分页是基本功任何列表查询都应该有分页兜底连接池打满是最危险的信号之一一个慢请求能把整个服务拖垮监控告警要配好P99超阈值就报警别等用户来反馈最后这种问题其实不算难排查关键是要有一个清晰的排查路径日志 → 调用链 → 监控 → 慢SQL → 数据分布一步一步缩小范围别一上来就怀疑框架、怀疑JVM、怀疑玄学。大部分线上问题根因都很朴素。