MySQL慢查询优化实战案例
本文记录一个真实的慢查询优化过程:某电商订单查询从10秒优化到100毫秒,性能提升100倍.
问题背景
运营后台的订单列表查询超时,用户抱怨无法使用:
-- 原始慢查询
SELECT * FROM orders
WHERE user_id = 12345
AND created_at >= '2026-01-01'
AND status IN ('paid', 'shipped')
ORDER BY created_at DESC
LIMIT 20;
-- 执行时间:10.2秒!
诊断过程
第1步:查看执行计划
EXPLAIN SELECT * FROM orders ... -- 输出: -- +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+ -- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | -- +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+ -- | 1 | SIMPLE | orders | ALL | NULL | NULL | NULL | NULL | 523456 | Using where | -- +----+-------------+--------+------+---------------+------+---------+------+--------+-------------+ -- 关键信息: -- type=ALL 表示全表扫描! -- rows=523456 扫描了52万行
第2步:分析表结构
SHOW INDEX FROM orders; -- 发现只有主键索引,没有其他索引! SHOW TABLE STATUS LIKE 'orders'; -- 表大小:2.1GB -- 行数:520万行
优化方案
方案1:添加联合索引(推荐)
-- 分析查询条件: -- WHERE user_id = ? AND created_at >= ? AND status IN ? -- ORDER BY created_at DESC -- 创建联合索引,注意字段顺序 -- 等值查询字段在前,范围查询在后,排序字段最后 ALTER TABLE orders ADD INDEX idx_user_created_status (user_id, created_at, status); -- 验证执行计划 EXPLAIN SELECT * FROM orders ... -- type=range, key=idx_user_created_status, rows=125 -- 扫描行数从52万降到125行!
方案2:覆盖索引优化(进阶)
-- 如果只需要特定字段,用覆盖索引避免回表 SELECT order_id, user_id, total_amount, status, created_at FROM orders WHERE user_id = 12345 AND created_at >= '2026-01-01' ORDER BY created_at DESC LIMIT 20; -- 创建覆盖索引 ALTER TABLE orders ADD INDEX idx_cover (user_id, created_at, status, order_id, total_amount); -- 执行计划显示:Extra=Using index(覆盖索引) -- 无需回表查数据,性能进一步提升
优化效果对比
| 阶段 | 执行时间 | 扫描行数 | 优化手段 |
|---|---|---|---|
| 优化前 | 10.2秒 | 523,456 | 无索引,全表扫描 |
| 加联合索引 | 0.8秒 | 125 | idx_user_created_status |
| 覆盖索引 | 0.1秒 | 125 | 覆盖索引避免回表 |
经验总结
- 慢查询先查执行计划 – EXPLAIN是优化的起点
- 索引字段顺序很重要 – 等值在前,范围在后
- 覆盖索引是利器 – 避免回表能大幅提升性能
- 定期分析慢查询日志 – 用pt-query-digest找出最耗时的SQL
延伸工具
# 开启慢查询日志 SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; -- 超过1秒的SQL记录 # 分析慢查询 pt-query-digest /var/lib/mysql/slow.log # 查看索引使用情况 SELECT * FROM sys.schema_unused_indexes WHERE object_schema = 'your_db';
总结
数据库优化没有银弹,但遵循”先诊断,后优化”的原则,大多数慢查询都能通过合理的索引设计解决.
