MySQL 慢查询优化实战:从慢日志、EXPLAIN 到索引与深分页改写
业务反馈接口变慢,监控里 RDS for MySQL 的 CPU 一路走高,这是很常见的场景。大多数情况下,罪魁祸首是少数几条没有走到合适索引的 SQL。这篇文章整理了一套从发现慢 SQL、用 EXPLAIN 分析,到改写和验证的流程,命令都可以直接复用。
一、先把慢 SQL 找出来
在华为云 RDS 控制台的「慢日志」页面可以查看慢 SQL 的统计,包括执行次数、平均耗时和扫描行数。如果想在实例里确认阈值,可以执行:
SHOW VARIABLES LIKE 'long_query_time'; SHOW VARIABLES LIKE 'slow_query_log';
排查时不要只盯着单次最慢的 SQL,更要看「总耗时 = 次数 × 平均耗时」最高的那几条,它们才是拖垮 CPU 的主力。
二、用 EXPLAIN 看执行计划
拿到 SQL 后,在测试环境或只读节点上执行:
EXPLAIN SELECT * FROM orders WHERE user_id = 1001 AND status = 'PAID' ORDER BY created_at DESC LIMIT 20;
重点看这几列:
- type:出现 ALL 表示全表扫描,index 表示全索引扫描,ref、range 才是比较健康的访问方式。
- key:实际使用的索引,为 NULL 说明没有用上索引。
- rows:预估扫描行数,与最终返回行数差距越大,浪费越多。
- Extra:出现 Using filesort 或 Using temporary,说明排序或分组需要额外开销。
三、常见的索引失效原因
1. 对索引列使用函数或计算。例如 WHERE DATE(created_at) = '2026-11-01',会让索引失效,应改成范围条件:
WHERE created_at >= '2026-11-01' AND created_at < '2026-11-02'
2. 隐式类型转换。手机号字段是 varchar,却用数字去比较 WHERE phone = 13800000000,MySQL 会把每行转换成数字再比较,索引同样用不上,应写成字符串 '13800000000'。
3. 模糊查询以通配符开头。LIKE '%abc' 无法利用 B+ 树索引,业务允许时改成前缀匹配,或引入专门的搜索方案。
4. 联合索引不满足最左前缀。已有索引 (user_id, status, created_at),如果查询只带 status 条件,就用不上这个索引。
5. 字符集或排序规则不一致。多表关联时,关联字段的字符集不同会造成转换,导致被驱动表走全表扫描。
四、针对性建立联合索引
上面那条查询按 user_id 等值过滤、status 等值过滤、再按 created_at 排序,合适的索引是:
ALTER TABLE orders ADD INDEX idx_user_status_created (user_id, status, created_at);
等值条件放前面,排序字段放最后,这样既能过滤又能避免 filesort。建索引前请注意:大表加索引会占用 IO 和 CPU,应选择业务低峰期,并确认磁盘空间充足;索引也不是越多越好,每多一个索引都会增加写入成本。
五、深分页的改写
类似 LIMIT 1000000, 20 的深分页,MySQL 需要先扫描并丢弃前一百万行。常见的改法是记录上一页最后一条的主键,用条件代替偏移量:
SELECT * FROM orders WHERE id > 1000000 ORDER BY id LIMIT 20;
如果必须支持跳页,可以先用覆盖索引查出主键,再回表取数据:
SELECT o.* FROM orders o JOIN (SELECT id FROM orders WHERE status='PAID' ORDER BY id LIMIT 1000000, 20) t ON o.id = t.id;
六、验证效果
改完之后不要只看 EXPLAIN,还要用真实数据验证:
- 对比改动前后同一条 SQL 的 rows 和实际耗时。
- 观察 RDS 监控中的 CPU 使用率、慢查询数量是否下降。
- 用 SHOW INDEX FROM orders 检查是否有重复或冗余的索引,必要时清理。
七、小结
慢 SQL 优化的思路并不复杂:先按总耗时排序找出重点,再用 EXPLAIN 判断是否走了索引,最后针对原因修改 SQL 或补充索引,并用监控验证。养成上线前对核心 SQL 做 EXPLAIN 的习惯,比出问题后再救火省事得多。
- 点赞
- 收藏
- 关注作者
评论(0)