你是否遇到过这样的场景:
一条看似简单的 SQL,查询几十条数据却跑了几十秒;
索引也加了,写法也没问题,就是慢;
数据量一大,系统就卡得不行……
这不是你不会写 SQL,而是你忽略了数据库在背后“怎么执行”它。
真正决定 SQL 快慢的,并不是你写出来的那条语句,而是数据库执行它的方式——也就是 执行计划(Execution Plan)。
今天我们就来聊聊:
执行计划到底是什么?
它为什么会让你的 SQL 慢下来?
如何正确使用 EXPLAIN?
实战中常见的 3 个索引失效场景
什么是执行计划?
执行计划是数据库优化器根据你的 SQL 生成的“执行路径”。
简单说,就是:
“我要这么查、从哪查、怎么查、用不用索引、要不要排序、是否创建临时表”等一系列动作的安排。
在 MySQL 中,你可以通过如下语句看到它
EXPLAIN SELECT * FROM 表名 WHERE 条件;
或者:
EXPLAIN ANALYZE SELECT ... -- MySQL 8.0+
这一步,就是你和数据库真正“对话”的开始。
一个简单查询的“意外慢查”案例
看起来很正常的 SQL:
SELECT * FROM orders WHERE user_id = 123 AND status = 'PAID';
你还贴心地加了索引:
CREATE INDEX idx_user_status ON orders(user_id, status);
结果执行计划一查:
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 'PAID';
输出内容大概是:
vbnet
type: ALL
rows: 1053247
key: NULL
Extra: Using where
???全表扫描?索引根本没用上!
为什么会这样?
三个让你 SQL 慢下来的执行计划陷阱
1. 索引顺序不当,最左前缀不满足
你加的是 (user_id, status)
没错,但如果你写的查询语句是这样:
WHERE status = 'PAID'
或者换了顺序,写成:
WHERE status = 'PAID' AND user_id = 123
都会导致 最左前缀原则失效,索引无法命中。
💡 索引组合字段要从左到右依次命中才有效。
2. 数据分布太集中,索引命中不划算
假设 90% 的订单 status 都是 'PAID',优化器可能判断:
与其走索引筛 90%,再回表拿数据,不如直接全表扫一遍快。
这种情况即使你建了索引,MySQL 也可能选择不用。
3. SELECT * 导致回表太重
你查询了所有字段,而索引中只包含部分字段。数据库查到索引命中的行后,还得“回表”到主表查全量字段。
回表是随机 IO,数据一多就慢得飞起。优化器可能直接判断:
回表太费,干脆直接全表扫。
💡 解决方法:
减少字段数量,只查需要的字段
使用 覆盖索引(SELECT 的字段完全包含在索引中)
如何看懂执行计划的重点字段?
用 EXPLAIN
查询后,会看到这些:
| id | ||
| type | ||
| key | ||
| rows | ||
| Extra | Using temporary和 Using filesort |
MySQL 优化器会根据成本估算(Cost-based)来决定是否使用索引,并不是什么“有索引就一定快”。
SQL 性能优化的实用建议
用
EXPLAIN
分析每一条慢查询不要随意使用
SELECT *建立索引时,要考虑字段顺序和选择性
索引字段不要使用函数(如
DATE(create_time)
会失效)结合业务场景,考虑覆盖索引或冗余字段
慢查询日志 + 执行计划联合分析,效果更佳




