mysql 8.0 支持多列混合方向降序索引,物理存储严格按定义排序,仅当 order by 字面完全匹配且 where 满足最左前缀时可跳过 filesort;5.7 不支持真降序索引,混合方向排序必触发 filesort。

ORDER BY 多列方向不一致时,MySQL 8.0 能跳过 Using filesort,5.7 几乎必然触发——但这不是“自动生效”,而是严格匹配下的物理能力释放。
降序索引不是语法糖,是叶子节点级物理排序
MySQL 5.7 允许写 CREATE INDEX idx ON t (a DESC, b ASC),但 SHOW CREATE TABLE 会悄悄抹掉 DESC,实际建出来仍是 (a ASC, b ASC)。8.0 则真正在 B+ 树叶子节点按声明方向物理存储:比如 (status ASC, updated_at DESC) 的索引,其数据页内记录就是先按 status 升序、每个 status 组内再按 updated_at 降序排列。
这意味着只要查询的 ORDER BY 顺序和方向与索引定义**字面完全一致**,就能直接扫描索引得到有序结果,无需额外排序。
- ✅ 匹配:
WHERE status = 'active' ORDER BY status, updated_at DESC→ 走索引免filesort - ❌ 不匹配:
ORDER BY status DESC, updated_at DESC(索引最左列是ASC,方向错) - ❌ 不匹配:
ORDER BY status ASC, updated_at DESC LIMIT 10 OFFSET 100(LIMIT+OFFSET不影响匹配,但分页深度大时仍可能因回表或缓冲区不足退化)
多列混合方向排序在 5.7 中根本无解
5.7 的组合索引只支持单向逻辑:要么全 ASC,要么靠后向扫描(Backward Index Scan)勉强应付单列 DESC。一旦出现混合方向,比如 ORDER BY a DESC, b ASC,而索引是 (a, b)(默认 ASC),优化器无法同时满足两个方向——它要么用索引扫 a,再对每个 a 值内的 b 手动倒序,要么放弃索引走 filesort。实测中后者更常见,且 EXPLAIN 明确显示 Using filesort 和 Using temporary。
8.0 的 (a DESC, b ASC) 索引则天然适配该场景,只要 WHERE 条件覆盖最左前缀(如 WHERE a > 100),就能直接输出 a 降序、b 升序的结果流。
- ⚠️ 注意空格和括号:索引定义是
(a DESC, b ASC),查询写成ORDER BY a DESC , b ASC(逗号后多空格)仍可匹配;但写成ORDER BY a DESC, UPPER(b) ASC就完全不匹配——函数调用必须一字不差 - ⚠️
WHERE必须满足最左前缀:若索引为(category_id, created_at DESC),查询ORDER BY created_at DESC单独出现,且无WHERE category_id = ?,则索引无法用于排序
别信“建了就快”,先看 EXPLAIN 的 Extra
8.0 的降序索引不是性能银弹。很多升级后反而变慢的案例,根源在于误判索引生效条件。关键验证点只有两个:
-
EXPLAIN结果中Extra字段不含Using filesort -
key列明确显示用了你建的那个DESC索引名
如果看到 key: NULL 或 Extra: Using filesort,说明没命中——可能是 WHERE 条件没覆盖最左列、ORDER BY 方向有偏差、或者 SELECT * 导致回表开销过大,让优化器主动弃用索引。
真正容易被忽略的是:8.0 在 SELECT * + 大字段(如 TEXT)场景下,更倾向“全字段排序”,sort_buffer_size 不够时立刻落盘为 Using disk sort,此时哪怕索引匹配,整体耗时也可能比 5.7 的双路排序还高。所以 DESC 索引提效的前提,是 SELECT 列尽量精简、最好能被索引覆盖。











