在我们日常优化 SQL 性能的过程中,经常会遇到这样的场景:一条查询语句同时包含了 WHERE 条件和 ORDER BY 排序。这时候很多人都会想:能不能这两个操作都走索引,一次性加速?
答案是:可以,但有条件!
今天我们就来系统讲清楚这个问题,避免踩坑。
为什么同时用上索引这么重要?
通常,MySQL 查询的两大“耗时点”就是:
- 过滤数据(WHERE)
- 排序数据(ORDER BY)
如果两个操作都能用上索引,那查询速度会快得多,尤其是在数据量大的时候能节省大量资源。反之,如果排序没命中索引,MySQL 会启用 filesort,那性能就会直线下降。
什么情况可以让 WHERE 和 ORDER BY 同时走索引?
我们归纳一下,要满足以下这些条件:
1. 创建联合索引,把涉及的字段都包含进去
比如有下面这样一条 SQL:
SELECT * FROM orders WHERE user_id = 123 ORDER BY create_time;
那就应该建这样一个联合索引:
CREATE INDEX idx_user_time ON orders(user_id, create_time);
只有联合索引才有可能同时加速筛选和排序。
2. WHERE 的字段要出现在联合索引的“最左前缀”
举个例子,如果你建的是 (user_id, create_time) 索引:
- WHERE user_id = ... 可以使用索引
- WHERE create_time = ... 索引用不上(不是最左前缀)
3. ORDER BY 的字段顺序必须与索引保持一致
继续用上面的例子:
ORDER BY create_time ASC -- 顺序一致,可以用索引排序
ORDER BY create_time DESC -- 顺序相反,索引排序失效
ORDER BY user_id, create_time -- 仍然可以
ORDER BY create_time, user_id -- 不符合索引顺序
索引顺序不一致,就无法利用索引来完成排序,MySQL 就只能用临时排序(filesort)。
4. WHERE 中不要使用“范围条件”打断索引
什么是“范围条件”?比如 >、<、BETWEEN、LIKE '%abc' 都算。
一旦你在中间用了范围条件,索引就不能继续向后使用了:
-- 有索引 (user_id, create_time)
WHERE user_id > 100 ORDER BY create_time
-- 索引在 user_id 这步就终止了,create_time 排序只能 filesort
一句话:范围查询会“截断”联合索引的后续字段使用。
怎么判断索引有没有被用上?
用 MySQL 提供的 EXPLAIN 分析:
EXPLAIN SELECT * FROM orders WHERE user_id = 123 ORDER BY create_time;
重点看这两个字段:
- key: 使用了哪个索引
- Extra: 有没有 Using filesort
只要没出现 Using filesort,就说明排序是通过索引完成的,说明成功命中!
常见误区提醒
场景 | 是否可以 |
WHERE 和 ORDER BY 使用了两个不同的单列索引 | 不行,MySQL 只能选择一个索引 |
WHERE 中用到了 LIKE '%abc' 模糊查询 | 会导致索引失效 |
ORDER BY 顺序和索引相反 | 不能使用索引排序 |
WHERE 完全匹配 + ORDER 顺序一致 | 最理想的情况 |
WHERE 用了 LIMIT 分页 | 有时更容易触发索引优化(尤其配合 ORDER) |
小结一句话
要想 WHERE 和 ORDER BY 同时使用索引,记住三个关键点:
建联合索引、顺序一致、避免范围条件打断!
掌握这套方法,不但能写出更快的 SQL,还能避免一些隐藏的性能陷阱。
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏或转发给你的 DBA 朋友~
也可以留言聊聊你在优化 SQL 的过程中踩过哪些坑!