首页 > Technology > 正文

MySQL 慢查询怎么办?排查与优化完整指南

fenij 2026-08-04 8 Technology

后台一个列表页要转七八秒,服务器 CPU 又没跑满,这种情况八成是某几条 SQL 在拖时间。慢查询本身不难修,麻烦的是不少站点压根没开慢查询日志,出了问题只能靠猜。下面按我自己排障的顺序走一遍。

第一步:把慢查询日志打开

先看当前状态:

SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';

没开就临时开,不用重启实例:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
SET GLOBAL log_queries_not_using_indexes = 'ON';

阈值我一般设 1 秒。流量大的站点先设 2 秒也行,别一上来就 0.1,日志能把磁盘写满。要长期生效就写进 my.cnf 的 [mysqld] 段再重启服务。log_queries_not_using_indexes 只在排查期开,查完记得关。

第二步:用工具聚合,别手翻日志

日志跑够半天再看。直接 tail 文本没意义,用自带工具:

mysqldumpslow -s t -t 10 /var/log/mysql/slow.log

-s t 按总耗时排序,-t 10 取前十条。有时候单条只要 0.8 秒不算慢,但一天跑了三万次,累积起来比那条 5 秒的报表 SQL 更值得改。装了 percona-toolkit 的话 pt-query-digest 报告更细:

pt-query-digest /var/log/mysql/slow.log > /tmp/digest.txt

第三步:EXPLAIN 看执行计划

拿到具体 SQL,前面加 EXPLAIN 跑一遍:

EXPLAIN SELECT * FROM orders WHERE user_id = 1024 AND status = 2 ORDER BY created_at DESC LIMIT 20;

重点盯三列。type 出现 ALL 就是全表扫描;rows 是预估扫描行数,几十万往上基本可以确定要加索引;Extra 里写着 Using filesort 或 Using temporary,说明排序、分组没走上索引,MySQL 在内存或磁盘里另开了临时表。

第四步:加对索引,而不是加满索引

上面那条 SQL 该建联合索引,顺序按「等值条件在前,排序字段在后」:

ALTER TABLE orders ADD INDEX idx_user_status_time (user_id, status, created_at);

几个反复踩的坑:字段上套函数索引就废了,DATE(created_at) = ‘2026-08-01’ 得改写成范围比较 created_at >= ‘2026-08-01’ AND created_at < ‘2026-08-02’;字符串字段传数字进去会触发隐式类型转换,同样不走索引;SELECT * 拉一堆用不上的大字段,回表开销白白多出来,能列字段就列字段。

索引不是越多越好。每个二级索引都拖慢写入,一张表超过五六个索引就该回头审一遍。

第五步:现场卡住时先止血

如果是线上救火,直接看当前连接:

SHOW FULL PROCESSLIST;
SELECT * FROM information_schema.innodb_trx ORDER BY trx_started;

确认某条查询确实在拖垮实例,先 KILL 掉释放资源,再回头慢慢改 SQL:

KILL 12345;

常见问题

问:开慢查询日志会不会拖慢数据库?
影响很小,写日志是追加操作。真正的风险是磁盘被写满,排查完把 log_queries_not_using_indexes 关掉并清理旧日志就行。

问:加了索引还是慢?
先用 EXPLAIN 的 key 列确认索引真被用上了,再看是不是分页太深。LIMIT 100000, 20 这种要改成基于上一页最大 id 的游标分页。

问:加内存能不能解决?
innodb_buffer_pool_size 调到物理内存的 60%–70% 确实有效果,但那是给合理的 SQL 提速。糟糕的全表扫描,加多少内存都救不回来。

你在排查慢查询时碰到过哪些奇怪的执行计划?欢迎到 fenij.com 留言说说你的场景,我看到会回。