MySQL 慢查询怎么定位?
更新时间:2026-09-18。问题驱动短答页。慢查询优化的第一步不是改 SQL,而是准确找到那条最拖慢系统的 SQL。
一句话回答
先开慢查询日志记录超过阈值的 SQL,用 pt-query-digest/mysqldumpslow 按「总耗时」聚合出 TOP SQL,再对每条用 EXPLAIN 看执行计划,判断问题是没走索引、回表过多、文件排序还是深分页/N+1,最后分类治理。
定位三步走
sql
-- 1) 开启慢日志并设阈值(线上建议 0.5~1s,先临时设 0 抓全部也行)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;
SET GLOBAL log_queries_not_using_indexes = ON; -- 没走索引的也记录
-- 慢日志文件位置
SHOW VARIABLES LIKE 'slow_query_log_file';bash
# 2) 聚合分析:按总响应时间排序,找出最值得优化的几条
pt-query-digest /var/lib/mysql/slow.log | less
mysqldumpslow -s t -t 10 slow.log # 没有 percona 工具时的替代sql
-- 3) 对 TOP SQL 看执行计划
EXPLAIN SELECT ... ;EXPLAIN 重点看哪几列
| 列 | 危险信号 | 含义 |
|---|---|---|
type | ALL(全表扫)、index(全索引扫) | 至少要到 ref/range,理想 const |
key | NULL | 实际没用到任何索引 |
rows | 预估扫描行数巨大 | 扫描多、返回少,索引选择性差 |
Extra | Using filesort、Using temporary | 额外排序/临时表;Using index 是好的覆盖索引 |
key_len 能判断联合索引实际用了几列;type/key_len/Extra 的完整解读见 MySQL EXPLAIN 深挖。
常见根因与对应治理
- 索引缺失/失效:补索引或改写条件(索引失效见下一篇「索引为什么失效」)。
- 深分页:
LIMIT 1000000,20越翻越慢,改游标/书签分页,见 深分页优化。 - 回表过多:用覆盖索引(
Using index)减少回表。 - N+1 查询:同一条参数化 SQL 在接口里被调成百上千次,见 什么是 N+1 查询。
- 服务端实时定位:
SHOW FULL PROCESSLIST看当前正在执行的长事务;SELECT * FROM sys.statement_analysis ORDER BY avg_latency DESC或performance_schema做在线聚合。
坑
- 只看「次数多」不看「总耗时」:一条偶发但跑 10s 的报表 SQL 危害大于大量 5ms 的查询,
pt-query-digest默认按总时间排,别只按 count 排。 - 慢日志阈值长期设成 10s:很多 0.8s 的高频慢查被漏掉,建议 0.5s 起步并结合
log_queries_not_using_indexes。 - 只优化单条、不看并发:单条不慢但 QPS 极高也会压垮库,结合 Buffer Pool 命中率与连接数一起看,见 MySQL InnoDB Buffer Pool。
索引为什么建了却不走见 MySQL 索引为什么失效;执行计划深度解读见 MySQL EXPLAIN 深挖。
总结
定位 MySQL 慢查询就是「开 slow log(阈值 0.5~1s、记录未走索引语句)→ 用 pt-query-digest 按总耗时聚合出 TOP SQL → EXPLAIN 看 type/key/rows/Extra 判断全表扫、回表、文件排序还是没走索引 → 对因用补索引/覆盖索引、游标分页、消除 N+1 治理」,线上还可用 processlist 与 sys/performance_schema 实时抓正在跑的语句,且务必按总耗时而非调用次数排优先级。