老板说接口太慢,我用一个索引让SQL快了100倍

5 阅读3分钟

本文分享一次真实的 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都能解决