如何定位慢 SQL:工具、流程与实战示例

如何定位慢 SQL:工具、流程与实战示例

一、什么是慢 SQL

慢 SQL 是指执行时间超过设定阈值的 SQL 语句。MySQL 默认阈值为 10 秒,生产环境通常设为 1-2 秒。慢 SQL 会导致:

  • 接口响应超时
  • 数据库连接池耗尽
  • 影响其他正常查询(锁等待、CPU 打满)

二、定位慢 SQL 的整体流程

复制代码
告警/用户反馈 → 确定慢接口 → 获取慢 SQL 文本 → EXPLAIN 分析 → 定位根因 → 制定优化方案
     ↑                                                                        ↓
 监控大盘 ←------------------------------------ 验证效果 ←--------------------------------- 上线 ←------------------------------------ 实施优化

注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

三、定位工具详解

3.1 MySQL 慢查询日志(Slow Query Log)

这是 MySQL 内置的最基础的慢 SQL 发现手段。

开启与配置
sql 复制代码
-- 查看当前状态
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';

-- 动态开启(无需重启,重启后失效)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1;            -- 超过 1 秒记录
SET GLOBAL log_queries_not_using_indexes = ON;  -- 未走索引的也记录

-- 持久化配置(my.cnf)
-- [mysqld]
-- slow_query_log = 1
-- slow_query_log_file = /var/log/mysql/slow.log
-- long_query_time = 1
-- log_queries_not_using_indexes = 1
日志内容示例
复制代码
# Time: 2026-08-04T11:44:01.123456Z
# User@Host: app_user[app_user] @ 10.0.1.100 [10.0.1.100]  Id: 12345
# Query_time: 5.432100  Lock_time: 0.000100  Rows_sent: 150  Rows_examined: 12800000
SET timestamp=1722769441;
SELECT orm.record_code, sdlr.logistics_code
FROM xxx_goods_record orm
INNER JOIN xxx_logistics_record sdlr
  ON orm.record_code = sdlr.record_code
WHERE orm.record_code = 'xx.20260730.001856.xx';

关键字段含义:

字段 含义 关注点
Query_time SQL 执行耗时 核心指标
Lock_time 等待锁的时间 高说明有锁争用
Rows_sent 返回给客户端的行数 与 Rows_examined 对比
Rows_examined 扫描的行数 远大于 Rows_sent 说明有优化空间
使用 mysqldumpslow 汇总分析
bash 复制代码
# 按平均耗时排序,取 TOP 10
mysqldumpslow -s at -t 10 /var/log/mysql/slow.log

# 按出现次数排序
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log

# 按总耗时排序(找到总影响最大的 SQL)
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

输出示例:

复制代码
Count: 356  Time=4.52s (1609s)  Lock=0.00s (0s)  Rows=12.0 (4272), app_user[app_user]@10.0.1.100
  SELECT ... FROM xxx_goods_record orm
  INNER JOIN xxx_logistics_record sdlr ON orm.record_code = sdlr.record_code
  WHERE orm.record_code = 'S'

3.2 EXPLAIN 执行计划

定位到慢 SQL 后,用 EXPLAIN 分析其执行路径。

基本用法
sql 复制代码
EXPLAIN SELECT o.order_no, d.product_name
FROM orders o
INNER JOIN order_details d ON o.id = d.order_id
WHERE o.status = 'PAID' AND o.create_time > '2026-01-01';
输出字段详解
复制代码
+----+-------------+-------+------+---------------+---------+---------+-------+--------+-------------+
| id | select_type | table | type | possible_keys | key     | key_len | ref   | rows   | Extra       |
+----+-------------+-------+------+---------------+---------+---------+-------+--------+-------------+
|  1 | SIMPLE      | o     | ref  | idx_status    | idx_status | 62   | const |  15000 | Using where |
|  1 | SIMPLE      | d     | ref  | idx_order_id  | idx_order_id| 4   | o.id  |      3 | NULL        |
+----+-------------+-------+------+---------------+---------+---------+-------+--------+-------------+

type 列(访问类型,从好到差)

级别 含义 示例
const / system 主键或唯一索引等值查询 WHERE id = 1
eq_ref JOIN 时被驱动表用主键/唯一索引 JOIN b ON a.id = b.id
ref 使用普通索引等值查询 WHERE status = 'PAID'
range 索引范围扫描 WHERE id > 100WHERE time BETWEEN ...
index 全索引扫描(扫描整棵索引树) 覆盖索引但无 WHERE
ALL 全表扫描(最差) 无索引可用

Extra 列(重点关注项)

含义 是否需要优化
Using index 覆盖索引,不回表 ✅ 好
Using where 在存储引擎层过滤后还需在 Server 层过滤 一般
Using filesort 无法利用索引排序,需要额外排序 ⚠️ 需关注
Using temporary 使用临时表(常见于 GROUP BY、DISTINCT) ⚠️ 需关注
Using join buffer JOIN 时被驱动表无索引,使用 Block Nested Loop ❌ 需优化
EXPLAIN 进阶:FORMAT=JSON
sql 复制代码
EXPLAIN FORMAT=JSON SELECT ...;

JSON 格式提供更详细的成本估算(cost_info),包括 read_cost、eval_cost、prefix_cost 等,适合深度分析。

EXPLAIN ANALYZE(MySQL 8.0.18+)
sql 复制代码
EXPLAIN ANALYZE SELECT o.order_no
FROM orders o WHERE o.status = 'PAID' LIMIT 100;

这会真正执行查询并返回每一步的实际耗时、实际行数(vs 预估行数),是最精确的分析方式。

3.3 Performance Schema

MySQL 内置的性能监控框架,可以统计 SQL 级别的执行信息。

sql 复制代码
-- 查看执行次数最多的 TOP SQL
SELECT DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT/1000000000 AS avg_ms,
       SUM_ROWS_EXAMINED, SUM_ROWS_SENT
FROM performance_schema.events_statements_summary_by_digest
ORDER BY AVG_TIMER_WAIT DESC
LIMIT 10;

-- 查看当前正在执行的慢查询
SELECT id, user, host, db, command, time, state, info
FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 2
ORDER BY time DESC;

3.4 SHOW PROFILE(定位 SQL 内部耗时分布)

sql 复制代码
-- 开启 profiling
SET profiling = 1;

-- 执行目标 SQL
SELECT * FROM orders WHERE status = 'PAID';

-- 查看概要
SHOW PROFILES;

-- 查看具体某条 SQL 的耗时分布
SHOW PROFILE FOR QUERY 1;

输出示例:

复制代码
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000021 |
| checking permissions | 0.000005 |
| Opening tables       | 0.000015 |
| init                 | 0.000018 |
| System lock          | 0.000007 |
| optimizing           | 0.000004 |
| statistics           | 0.000012 |
| preparing            | 0.000008 |
| Sorting result       | 0.000003 |
| executing            | 0.000001 |
| Sending data         | 4.312500 |  ← 瓶颈在这里:数据读取/传输
| end                  | 0.000004 |
| query end            | 0.000003 |
| closing tables       | 0.000005 |
| freeing items        | 0.000012 |
+----------------------+----------+

3.5 APM(应用性能监控)系统

在微服务架构中,通常通过 APM 系统来关联 HTTP 请求与底层 SQL。

常见 APM 工具
工具 特点
SkyWalking 开源,Java Agent 无侵入接入,支持 SQL 参数抓取
Pinpoint 开源,韩国 Naver 出品,UI 直观
Arthas (阿里) 在线诊断工具,可动态追踪方法耗时
阿里云 ARMS 商业版,集成告警和 SQL 分析
CAT (大众点评) 开源,实时监控
APM 定位流程示例(以 SkyWalking 为例)
复制代码
1. 告警:接口 /xxx-logistics-info P99 > 3s
2. 打开 SkyWalking UI → Trace 查询 → 按接口路径过滤
3. 选择一条慢请求的 Trace
4. 查看 Span 瀑布图:
   ├─ HTTP GET /xxx-logistics-info     [3200ms]
   │  ├─ MyBatis: findByRecordCode               [12ms]
   │  ├─ MyBatis: findByMemberIdAndRecordCode    [8ms]
   │  └─ MyBatis: listStoreLogisticsInfoByReceivingRecordCode  [3150ms] ← 瓶颈
   │       └─ MySQL: SELECT orm.record_code...   [3148ms]
5. 复制完整 SQL → 用 EXPLAIN 分析
从 trace-id 关联 SQL

生产环境中,SQL 前通常会注入 trace 信息:

sql 复制代码
/* xxx-request-id:276077ca1786xxxx
   trace-id:276077ca1786068xxxx
   rpc-id:0.1.1 */
SELECT orm.record_code ...

这样在慢查询日志中可以直接通过 trace-id 关联到具体的用户请求。

3.6 数据库管理平台

企业级数据库管理平台通常集成了慢 SQL 的自动采集与分析:

平台 功能
Archery 开源 SQL 审核平台,集成慢日志分析
DMS (阿里云) SQL 审计、慢日志、锁分析一体化
Yearning 开源 SQL 审核,支持慢查询展示
自建 ELK 将慢日志导入 Elasticsearch,用 Kibana 可视化

四、实战定位流程

场景:线上接口超时告警

Step 1:确认告警信息
复制代码
告警内容:接口 /api/composite/stock/logistics/xxx-xxxx-logistics-info
P99 延迟 5.4s,超过阈值 2s
告警时间:2026-08-04 11:44
影响范围:所有调用该接口的用户
Step 2:通过 APM 定位慢 SQL

从 APM 的 Trace 详情中获取:

  • 慢 SQL 全文
  • 执行耗时:5.43s
  • 扫描行数:12,800,000
Step 3:EXPLAIN 分析
sql 复制代码
EXPLAIN SELECT orm.record_code as orderCode, ...
FROM xxx_goods_record orm
INNER JOIN xxx_logistics_record sdlr
  ON orm.record_code = sdlr.record_code AND orm.member_id = sdlr.member_id
LEFT JOIN xxxx_logistics_record_item sdlri
  ON sdlr.id = sdlri.store_receiving_logistics_record_id
WHERE orm.record_code = 'xxx.20260730.001856.xxx'
ORDER BY sdlr.create_time DESC, sdlri.state_time ASC;

分析结果可能显示:

  • receiving_goods_record 走了唯一索引(type=const),本身不慢
  • 但 JOIN 时 store_receiving_logistics_record 的关联条件涉及 record_code + member_id,如果联合索引不匹配,可能产生额外扫描
  • 三表 JOIN 后排序 Using filesort
Step 4:确定优化方向

通过表结构分析发现 sdlr 表本身有 record_codeorder_code 字段,可以不关联 orm 表。

Step 5:验证数据一致性
sql 复制代码
-- 确认两表字段数据完全一致
SELECT COUNT(*) FROM receiving_goods_record orm
INNER JOIN xxx_logistics_record sdlr
  ON orm.record_code = sdlr.record_code AND orm.member_id = sdlr.member_id
WHERE orm.order_code != sdlr.order_code;
-- 结果: 0
Step 6:实施优化并验证

修改 SQL → 本地测试 → 灰度上线 → 观察监控指标。

五、日常巡检:主动发现慢 SQL

除了被动告警,还需要定期主动巡检:

sql 复制代码
-- 1. 查看当前有哪些长事务在跑
SELECT trx_id, trx_state, trx_started, trx_query, trx_rows_locked
FROM information_schema.innodb_trx
WHERE trx_state = 'RUNNING' AND TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 5;

-- 2. 查看表的索引使用情况(哪些索引从未被使用)
SELECT object_schema, object_name, index_name
FROM performance_schema.table_io_waits_summary_by_index_usage
WHERE index_name IS NOT NULL AND count_star = 0
AND object_schema = 'your_database';

-- 3. 查看哪些表全表扫描最多
SELECT object_schema, object_name, count_read AS full_scan_count
FROM performance_schema.table_io_waits_summary_by_table
WHERE count_read > 100000
ORDER BY count_read DESC LIMIT 10;

六、总结对照表

场景 推荐工具 定位精度
开发/测试环境调试 EXPLAIN + SHOW PROFILE SQL 级别
生产环境定位 慢查询日志 + mysqldumpslow SQL 级别
微服务链路追踪 APM (SkyWalking/Pinpoint) 请求 → SQL 全链路
实时在线诊断 Arthas / SHOW PROCESSLIST 即时
长期趋势分析 Performance Schema / ELK 统计维度
锁问题排查 INNODB_TRX + INNODB_LOCKS 事务/锁级别
相关推荐
一笑的小酒馆3 小时前
Android视频直播播放器简单封装
android
雨晨源码(同名B站)3 小时前
基于深度学习YoloV11农业病害虫害检测系统 智慧农业信息化综合管理平台 (附源码+lw文档+ppt)
数据库·人工智能·深度学习·yolo·信息可视化
盗理者4 小时前
AI Agent 技能分享|SQL 性能诊断与优化
java·sql·spring·skill
DBA小马哥4 小时前
向量数据库入门到进阶:Embedding、ANN算法与RAG落地的关键术语
数据库·算法·embedding
云和数据.ChenGuang4 小时前
fastapi项目拆分实战数据模型
java·服务器·数据库·人工智能·深度学习·fastapi·强化学习
祈禾4 小时前
Redis三大特殊数据类型
运维·服务器·数据库·redis·笔记·缓存
东方护航数据恢复(深圳)4 小时前
MySQL_Oracle数据库崩溃修复全攻略_东方护航数据恢复深圳店
数据库·mysql·oracle
工会代表4 小时前
安卓手机搭建SOCKS5代理教程
android
雨白4 小时前
Android AOP 切面编程实战:优雅地处理全局断网拦截
android·架构
y = xⁿ5 小时前
一文掌握Redis常见八股
数据库·redis·缓存