mysql 8.0 降序索引仅在 order by 方向与索引定义逐列严格一致、where 满足最左前缀且无函数表达式时才跳过 filesort;否则不生效甚至更慢。

MySQL 8.0 并不天然比 5.7 排序更快——只有在明确使用 DESC 显式声明的降序索引,且查询恰好匹配该方向时,才可能跳过 filesort;否则反而更慢。
MySQL 8.0 的 DESC 索引不是“自动加速”,而是“有条件生效”
MySQL 8.0.13+ 支持在 CREATE INDEX 中显式写 DESC,比如:CREATE INDEX idx_status_created ON orders (status, created_at DESC)。但这不等于“建了就快”:
- 必须是
ORDER BY status, created_at DESC这种**完全一致的顺序和方向**,才能走索引免排序 - 如果写成
ORDER BY status ASC, created_at DESC(哪怕 ASC 是默认值),优化器仍可能识别为匹配;但写成ORDER BY status DESC, created_at DESC就完全不匹配——因为索引最左列是ASC(未声明即默认) - MySQL 5.7 对
DESC索引声明直接忽略,建了也当ASC存,所以老版本里加DESC没意义 - 执行前务必用
EXPLAIN看Extra是否还有Using filesort,不能凭空假设
为什么没加 DESC 索引时,8.0 反而更慢?
这不是 bug,是行为变更带来的隐性成本:
-
max_length_for_sort_data参数在 8.0.20+ 被移除,导致 MySQL 更倾向使用“全字段排序”(即把所有 SELECT 列都拉进sort_buffer排),而不是 5.7 偏好的“行ID排序 + 回表” - 当你的
SELECT *包含大字段(如TEXT、JSON),8.0 会尝试把整行塞进内存排序缓冲区;超限时立刻落盘,出现Using disk sort,性能断崖下跌 - 5.7 在同样场景下可能选更保守的“双路排序”,反而避免了磁盘 I/O
- 验证方式:
SHOW STATUS LIKE 'Sort_merge_passes'升高,或EXPLAIN FORMAT=JSON中看到using_filesort: true且using_disk_sort: true
真正能靠降序索引提效的典型场景
只适用于「过滤 + 严格单向排序」的高频查询,例如后台分页列表:
- 常见语句:
SELECT id, title, updated_at FROM posts WHERE category_id = ? ORDER BY updated_at DESC LIMIT 20 - 对应索引:
CREATE INDEX idx_cat_updated ON posts (category_id, updated_at DESC) - 关键点:WHERE 条件字段在前(满足最左前缀),ORDER BY 字段紧接其后且方向一致;同时
SELECT只取索引覆盖的列,避免回表 - 若业务还需支持
ASC分页,不要强行复用同一索引——要么建两个((category_id, updated_at ASC)和(category_id, updated_at DESC)),要么接受其中一种方向走filesort
别被“8.0 支持 DESC 索引”带偏,先确认是否真需要它
很多团队升级后变慢,就是因为盲目给所有排序字段加 DESC,却忽略了实际查询模式:
- 主键本身天然支持双向扫描,
ORDER BY id DESC在任何版本都不需要额外索引 - 时间字段如果几乎总是
DESC,建降序索引合理;但如果既有ASC(如“最早订单”)又有DESC(如“最新订单”),优先保证高频方向,低频方向用其他手段(如应用层缓存结果) - 联合索引中混用
ASC/DESC(如(a ASC, b DESC))仅在 8.0+ 有效,5.7 会退化为全表扫描——跨版本兼容时要格外小心 - 最常被忽略的一点:即使建了完美匹配的降序索引,如果
WHERE条件用了函数(如WHERE DATE(created_at) = '2026-05-01'),整个索引立即失效











