后台一个列表页要转七八秒,服务器 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 留言说说你的场景,我看到会回。