mysql 8.0降序索引可优化order by col desc查询,但需满足最左前缀、方向一致、覆盖查询三条件;否则仍触发using filesort。

ORDER BY id DESC 不走索引?先看执行计划里有没有 Using filesort
MySQL 8.0 的降序索引不是“锦上添花”,而是解决 ORDER BY ... DESC 查询卡顿的刚需。核心判断标准就一条:执行 EXPLAIN SELECT ... ORDER BY col DESC 后,Extra 字段里不能出现 Using filesort。只要它还在,说明 MySQL 还得把数据捞出来再排序,哪怕你建了索引也没用。
常见错误现象:
- 明明建了
INDEX(status, id DESC),但SELECT * FROM t WHERE status = 1 ORDER BY id DESC仍触发Using filesort -
WHERE status IN (1,2)或status > 0时,id DESC部分完全失效,索引只用于过滤,不用于排序
根本原因在于:只有等值条件(=、IS NULL)能“锚定”最左前缀,让后续列的排序方向生效;范围查询会中断排序能力的传递。
多列混合方向索引怎么写才真正生效
联合索引中 ASC/DESC 不是随意搭配的,它直接决定 B+ 树叶子节点的物理扫描顺序。MySQL 只能按索引定义的“自然方向”前向扫描,无法中途翻转。
实操建议:
-
CREATE INDEX idx ON t(a ASC, b DESC)支持:ORDER BY a ASC, b DESC和ORDER BY a ASC;但不支持ORDER BY a ASC, b ASC -
CREATE INDEX idx ON t(a DESC, b ASC)支持:ORDER BY a DESC, b ASC;但不支持ORDER BY a DESC, b DESC - 两列都
DESC(INDEX(a DESC, b DESC))只优化ORDER BY a DESC, b DESC场景,别指望它也能加速ASC查询 - MySQL 8.0 禁止对同一列重复指定方向,比如
(a ASC, a DESC)会直接报语法错误
容易踩的坑:有人以为 INDEX(a, b DESC) 能同时覆盖 ORDER BY a, b DESC 和 ORDER BY a, b ASC —— 实际上它俩物理顺序相反,不可能共用一个索引。
为什么降序索引能减少扫描行数
本质是前向扫描比后向扫描快。MySQL 5.7 及以前,即使写 ORDER BY id DESC,也得用升序索引 + 从末尾倒着读,I/O 效率低;MySQL 8.0 的降序索引是真实把键值按降序存进 B+ 树,优化器能直接从前向后顺序读取,避免跳页、反向指针等开销。
性能影响示例:
- 8 万行测试数据下,
ORDER BY price DESC在 5.7 中rows显示 79999,且带Using filesort - 同样查询在 8.0 +
INDEX(price DESC)下,rows降到 5,Extra干净无排序标记
注意:这个优势只在排序字段参与索引定义且方向匹配时才成立。如果查询还涉及 SELECT * 且索引不覆盖,回表后结果集不再有序,MySQL 仍会补上 Using filesort。
覆盖查询 + 降序索引 = 零排序 + 零回表
这是降序索引最有价值的落地场景:当 SELECT 列全部落在索引中,且 WHERE 条件满足最左前缀,ORDER BY 方向又和索引一致,就能一步到位。
例如:
- 表
orders(status, created_at DESC, id)存在 - 查询
SELECT id FROM orders WHERE status = 'paid' ORDER BY created_at DESC—— 完全命中索引:过滤、排序、覆盖三重生效 - 但如果改成
SELECT * FROM ...,就会触发回表,而回表后的id是乱序的,MySQL 必须再排一次,Using filesort回来
容易被忽略的关键点:NULL 值在 DESC 索引中默认排最前面(不是最后),如果你业务依赖 NULL 排序位置,得额外加 IS NOT NULL 条件或调整应用逻辑。











