MySQL慢查询优化实战:从3秒到30毫秒
Mei Lin | 2026-07-29T13:28:00 | Database
一个真实的MySQL慢查询优化案例,通过EXPLAIN分析、索引优化和SQL改写将查询时间从3秒降到30毫秒
# MySQL慢查询优化实战:从3秒到30毫秒 ## 背景 最近接到一个工单,某个报表页面加载需要10秒+,排查发现是一条SQL慢查询导致的,执行时间3.2秒。经过优化最终降到30毫秒,提升了100倍。 ## 问题SQL ```sql SELECT o.*, u.username, u.email FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE o.status = 1 AND o.created_at BETWEEN '2024-01-01' AND '2024-06-30' ORDER BY o.created_at DESC LIMIT 20; -- 执行时间:3.2秒 ``` ## EXPLAIN分析 ```sql EXPLAIN SELECT ... -- type: ALL(全表扫描!) -- rows: 2,340,567(扫描了230万行) -- Extra: Using where; Using filesort(文件排序!) ``` 问题很明显:**全表扫描 + 文件排序**。 ## 优化过程 ### 第一步:添加组合索引 ```sql ALTER TABLE orders ADD INDEX idx_status_created (status, created_at); -- 再次EXPLAIN -- type: range(范围扫描) -- rows: 45,230(大幅减少) -- 执行时间:800毫秒 ``` ### 踩坑:索引顺序 > 一开始我把索引建成了 `(created_at, status)`,结果查询根本没走索引。因为WHERE中status是等值查询,应该放在前面! ```sql -- 错误:created_at在前 -- INDEX (created_at, status) 不会用到status的过滤 -- 正确:等值查询字段在前,范围查询字段在后 -- INDEX (status, created_at) 完美匹配 ``` ### 第二步:SQL改写 ```sql -- 用子查询先分页再JOIN SELECT o.*, u.username, u.email FROM ( SELECT id FROM orders WHERE status = 1 AND created_at BETWEEN '2024-01-01' AND '2024-06-30' ORDER BY created_at DESC LIMIT 20 ) t JOIN orders o ON t.id = o.id LEFT JOIN users u ON o.user_id = u.id; -- 执行时间:30毫秒! ``` ## 优化结果 | 步骤 | 执行时间 | 提升 | |------|----------|------| | 原始 | 3.2秒 | - | | 加组合索引 | 800ms | 4x | | SQL改写 | 30ms | 106x | ## 总结 1. 先用EXPLAIN看执行计划 2. 索引顺序:等值在前,范围在后 3. 大分页用延迟关联(先取ID再JOIN)