TECH DIARY
线上慢SQL排查思路全攻略
2026年7月28日 · 先止损再定位后根治防复发 · IT中华技术日记
IT中华 本文整理线上慢 SQL 排查的黄金四步流程:先止损 → 再定位 → 后根治 → 防复发,附 SpringBoot+Druid 监控代码与常见根因对照表。
第一步:紧急止损
- 确认影响范围:APM 工具(SkyWalking/Pinpoint)定位慢接口
- 快速熔断降级:核心接口被打挂时降级非核心功能
- Kill 异常慢查询:
show full processlist;找到耗时 >10s 线程,kill [id]; - 流量切分:读压力切从库,写压力暂关非核心写
第二步:精准定位
数据库层面(80%问题在这里)
-- 当前运行线程 show full processlist; -- 慢查询日志(阈值建议1s) show variables like 'slow_query_log'; show variables like 'long_query_time'; -- 分析执行计划(最关键) explain select * from user where name = '张三'; -- 关注 type / key / rows / Extra
应用层面(剩余20%)
- N+1 查询:循环调用数据库查关联表
- 大分页:
limit 1000000, 10扫描前百万行 - 长事务/大事务:占用连接,导致其他请求排队
第三步:针对性根治
| 常见原因 | 解决方案 |
|---|---|
| 全表扫描 | 创建联合索引,遵循最左前缀 |
| 索引失效 | 避免函数/运算/隐式转换/like '%x' |
| 大分页 | 游标分页:where id > last_id limit 10 |
| N+1查询 | 批量查询 where id in (...) |
| 大事务 | 拆分事务,移出非DB操作 |
第四步:防复发
- 上线前 SQL 审核:Druid、SonarQube 自动检测
- 压测验证:全链路压测模拟真实流量
- 监控告警:慢查询超 1s 自动报警
- 定期巡检:每月清理无用索引,优化表结构
技术亮点代码
大分页优化(性能提升1000倍)
// ❌ 扫描前100万行
selectPage(offset, pageSize);
// ✅ 游标分页(基于自增ID)
@Select("select id,name,phone from user " +
"where id > #{lastId} order by id asc limit #{pageSize}")
List<User> selectByCursor(@Param("lastId") long lastId, @Param("pageSize") int pageSize);
N+1 查询根治
// ❌ N次查用户
for (Order o : orders) {
User u = userMapper.selectById(o.getUserId());
}
// ✅ 批量查询(1次)
Set<Long> ids = orders.stream().map(Order::getUserId).collect(toSet());
Map<Long,User> map = userMapper.selectByIds(ids).stream()
.collect(toMap(User::getId, identity()));
IT中华 小结:监控先发现 → 工具抓SQL → EXPLAIN看计划 → 结合场景挖根因 → 建索引/改写法/调架构 → 验证闭环。永远先观测再动手,线上最怕凭直觉改代码。隐式类型转换(varchar字段传int值)导致索引失效,是最高频的坑。
关于IT中华
IT中华(www.itzh.vip)持续关注后端开发与数据库性能。本文转载改写自博客园 Rain 的 Java 大神实战圈技术文章。关注IT中华,获取更多实战排查经验。
IT中华 · www.itzh.vip · 技术日记