本文分享一次真实的 SQL 优化经历,从查询耗时 3 秒优化到 30 毫秒,性能提升 100 倍。
一、背景
在我们的电商系统中,有一张订单表:
loans_merchant_mortgage_order
数据量:
约 380 万条
业务需求:
根据 merchant_id 查询订单列表:
SELECT *
FROM loans_merchant_mortgage_order
WHERE merchant_id = 2011360956409622528
AND delete_status = 0
ORDER BY create_time DESC;
看起来是一个非常普通的 SQL。
但执行时间:
3.2 秒 😨
接口直接超时。
二、问题分析
使用:
EXPLAIN SELECT ...
执行计划如下:
type: ALL
key: NULL
rows: 3800000
Extra: Using where; Using filesort
关键问题:
| 问题 | 含义 |
|---|---|
| type = ALL | 全表扫描 |
| key = NULL | 没用索引 |
| rows = 380万 | 扫描全部数据 |
| Using filesort | 额外排序 |
也就是说:
MySQL 在扫整张表 😨
三、为什么索引没有生效?
表结构:
CREATE TABLE loans_merchant_mortgage_order (
id BIGINT PRIMARY KEY,
merchant_id BIGINT,
delete_status TINYINT,
create_time DATETIME
);
已有索引:
idx_merchant_id (merchant_id)
看起来没问题。
但问题在于:
WHERE merchant_id = ?
AND delete_status = ?
ORDER BY create_time DESC
涉及:
merchant_id
delete_status
create_time
而索引只有:
merchant_id
MySQL需要:
- 先过滤 merchant_id
- 再过滤 delete_status
- 再排序 create_time
导致:
索引无法覆盖排序
触发:
Using filesort
性能暴跌。
四、正确优化方案:建立联合索引
创建联合索引:
CREATE INDEX idx_merchant_del_create
ON loans_merchant_mortgage_order
(merchant_id, delete_status, create_time DESC);
再执行:
EXPLAIN SELECT ...
结果:
type: ref
key: idx_merchant_del_create
rows: 42
Extra: Using index condition
关键变化:
| 优化前 | 优化后 |
|---|---|
| 全表扫描 | 索引扫描 |
| 扫描 380万 | 扫描 42 |
| Using filesort | 无 |
五、性能对比
优化前:
耗时:3.2 秒
优化后:
耗时:0.03 秒
提升:
100 倍 🚀
六、为什么联合索引这么重要?
MySQL 遵循:
最左前缀原则
索引:
(merchant_id, delete_status, create_time)
支持:
WHERE merchant_id = ?
WHERE merchant_id = ? AND delete_status = ?
WHERE merchant_id = ? AND delete_status = ? ORDER BY create_time
完美匹配查询。
MySQL可以:
直接从索引获取有序数据
无需排序
七、SQL优化核心原则总结(重点)
原则 1:WHERE字段必须加索引
WHERE merchant_id = ?
必须有:
INDEX(merchant_id)
原则 2:ORDER BY 字段要加入联合索引
错误:
INDEX(merchant_id)
正确:
INDEX(merchant_id, create_time)
原则 3:避免 Using filesort
看到:
Using filesort
就要警惕性能问题。
原则 4:避免 SELECT *
错误:
SELECT *
正确:
SELECT id, merchant_id, create_time
减少回表。
八、进阶优化:覆盖索引(性能更高)
创建:
CREATE INDEX idx_cover
ON loans_merchant_mortgage_order
(merchant_id, delete_status, create_time, id);
查询:
SELECT id
FROM loans_merchant_mortgage_order
WHERE merchant_id = ?
AND delete_status = 0
ORDER BY create_time DESC;
Extra:
Using index
无需回表,速度最快。
九、SQL优化万能排查流程(建议收藏)
1. 看慢SQL日志
2. 使用 EXPLAIN 分析
3. 看 type 是否 ALL
4. 看 key 是否 NULL
5. 看 rows 数量
6. 看 Extra 是否 filesort
7. 创建联合索引
8. 再次 EXPLAIN
十、一个重要认知误区
很多人以为:
有索引 = 快
错误。
正确是:
合适的索引 = 快
错误索引:
INDEX(a)
INDEX(b)
正确索引:
INDEX(a,b)
十一、实战经验总结(核心)
SQL慢,90%原因:
索引没建对
优化核心就一句话:
让SQL走索引,并减少扫描行数
十二、优化效果总结
| 优化前 | 优化后 |
|---|---|
| 全表扫描 | 索引扫描 |
| 380万扫描 | 42行扫描 |
| 3.2秒 | 0.03秒 |
| Using filesort | 无 |
性能提升:
100倍 🚀
十三、推荐大家必须掌握的 SQL 优化技能
必会:
EXPLAIN
联合索引
覆盖索引
最左匹配原则
避免filesort
避免全表扫描
十四、结尾
SQL优化不是玄学,本质就一句话:
减少MySQL扫描的数据量
当你掌握联合索引后:
99%慢SQL都能解决