mysql 8.0降序索引仅在where等值匹配最左前缀、order by方向与索引逐列严格一致、select列被索引覆盖时才跳过filesort;否则无效甚至更慢,需通过explain验证extra字段是否消失。

MySQL 8.0 的降序索引本身不会“自动大幅提升查询性能”——它只在极少数严格匹配的场景下跳过 Using filesort,否则可能更慢,甚至白建。
降序索引生效必须满足三个硬性条件
缺一不可,否则 EXPLAIN 里照样出现 Using filesort:
- WHERE 条件必须提供索引最左列的等值匹配(
=或IS NULL),比如索引是(status, created_at DESC),就得写WHERE status = 'done';用WHERE status IN (...)或status > 10就断掉排序能力 - ORDER BY 的列顺序、方向必须和索引定义逐列完全一致:建了
(a ASC, b DESC),就只认ORDER BY a, b DESC;写成ORDER BY a ASC, b DESC(显式加 ASC)通常仍可识别,但ORDER BY a DESC, b DESC就完全不匹配 - SELECT 列最好被索引覆盖(避免回表),尤其别用
SELECT *——如果包含大字段(如TEXT),MySQL 8.0 更倾向全字段排序,容易触发Using disk sort
为什么匹配时能省掉 filesort?
本质不是“更快”,而是“少做一件事”:
- MySQL 5.7 对
ORDER BY x DESC只能靠升序索引 + 后向扫描(从 B+ 树叶子节点末尾往前读),I/O 跳页多、缓存局部性差 - MySQL 8.0 的降序索引把键值真实按降序存进 B+ 树,优化器能从前向后顺序读取,物理顺序 = 逻辑顺序,无需额外排序步骤
- 效果体现在
EXPLAIN的Extra字段消失Using filesort,且type是index或range,rows显著减少
建了降序索引反而变慢的常见原因
很多人没意识到这是设计代价,不是 bug:
- 空间翻倍:
INDEX (a ASC)和INDEX (a DESC)是两套独立物理结构,InnoDB 不复用;联合索引中混合方向(如(a ASC, b DESC))也不能替代(a DESC, b ASC) - 维护开销增加:每次 INSERT/UPDATE 都要按反向规则编码键值,对高写入表有轻微影响
- 优化器误选:当统计信息不准或查询带函数(如
ORDER BY DATE(created_at) DESC),降序索引直接失效,且无法 fallback 到升序索引,只能全表扫 + filesort -
max_length_for_sort_data在 8.0.20+ 被移除,导致排序更激进——若 SELECT 大字段,sort_buffer不够就落盘,比 5.7 的双路排序还慢
真正该优先检查的,往往不是索引方向
多数标称“需要降序索引”的慢查询,根子不在 DESC 上:
- 先确认 WHERE 是否漏掉了高选择性等值条件:比如
WHERE category_id = ? ORDER BY updated_at DESC,索引应是(category_id, updated_at DESC),而不是只给updated_at建单列 DESC 索引 - 查是否有隐式类型转换:
WHERE id = '123'(id 是 INT)会让整个索引失效,再准的 DESC 定义也救不了 - 用
sys.schema_unused_indexes看看现有索引哪些根本没人用,别一边建新索引一边养废索引 - 如果业务既要
ASC分页又要DESC分页,建两个方向的联合索引比强行共用一个更靠谱
降序索引不是开关,是精密配件——装错位置、拧错扭矩,机器照常转,只是效率更低。验证它是否起效,唯一可靠动作是跑 EXPLAIN 看 Extra 字段,而不是看建表语句里有没有写了 DESC。











