
线上接口突然变慢,往往是少索引的 SQL。慢查询日志开销极低,是定位慢 SQL 的首选。本文按「开参数 → 捞日志 → EXPLAIN 定位 → 改索引」给出完整流程。
一、开启慢查询日志与关键参数
1. 三个必须调对的参数
long_query_time 默认 10 秒,对线上过于宽松,一般调到 0.5 到 1 秒。slow_query_log 是总开关,slow_query_log_file 指定路径。最容易漏掉的是 log_queries_not_using_indexes,它会把未走索引的语句全写进日志,并发高的库很快写满磁盘,只建议排查期临时开启。
2. 动态开启与生效范围
这些参数无需重启即可生效,但重启后丢失,必须同步写入配置文件。
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
SET GLOBAL 只对之后新建的连接生效,当前会话读到的仍是旧值,需重连后用 SHOW VARIABLES LIKE 'long_query_time' 确认。
二、用聚合工具捞出最耗时的 SQL
1. mysqldumpslow 按耗时排序
原始日志动辄几百 MB,逐行翻没有意义。mysqldumpslow 可按耗时、次数排序,并把数字与字符串归一化为占位符,同结构语句不会被拆散。
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
mysqldumpslow -s at -t 20 -g 'SELECT' /var/log/mysql/slow.log
-s 取值中 t 是累计耗时,c 是次数,at 是平均耗时;排查「平时正常、偶尔卡住」用 at 更准。
2. pt-query-digest 给出更细画像
Percona Toolkit 的 pt-query-digest 按响应时间占比排序,并给出扫描行数与返回行数。
pt-query-digest --limit 10% /var/log/mysql/slow.log > report.txt
扫描行数与返回行数的比值超过 100 倍,基本可判定索引选择性太差。
三、定位之后如何改写与验证
1. 用 EXPLAIN 看执行计划
先跑 EXPLAIN 看 type、key、rows。type 为 ALL 是全表扫描,理想值是 ref 或 range;key 为 NULL 说明没走索引;rows 越大越危险。
EXPLAIN SELECT id, title FROM article
WHERE category_id = 12 AND status = 1
ORDER BY created_at DESC LIMIT 20;
2. 联合索引与隐式转换
最典型的是排序字段没进索引。上面若只在 category_id 建单列索引,MySQL 仍需对结果做文件排序;改成联合索引 (category_id, status, created_at),列顺序与 WHERE、ORDER BY 一致即可按索引有序返回。
第二个坑是隐式类型转换:字段是 varchar 却传数字,索引会失效,必须写成 WHERE phone = '13800000000'。范围查询会截断联合索引,等值条件应放前面。
关键配置提示
- 生产库
long_query_time取 0.5 到 1 秒,log_queries_not_using_indexes只在排查期临时开。 - 慢日志超过 1GB 先按天切分归档,单文件过大解析极慢。
- 新增索引后低峰期用
EXPLAIN复验,确认rows下降再保留。

快来评论,快来抢沙发吧~