Using filesort出现说明降序索引未生效,根本原因是WHERE条件未构成最左前缀等值过滤,导致ORDER BY方向无法被索引利用;混合方向索引必须严格匹配查询顺序,且不支持部分复用。

EXPLAIN里出现Using filesort,说明降序索引没生效
只要 EXPLAIN 的 Extra 字段里还带着 Using filesort,不管建了多少个 DESC 索引,MySQL 都没跳过排序步骤。这不是索引建得不够多的问题,而是查询条件和索引定义不匹配。
常见错误现象:
-
WHERE status IN (1, 2) ORDER BY created_at DESC——IN是范围条件,无法“锚定”最左前缀,created_at DESC部分完全失效 -
WHERE user_id > 100 ORDER BY id DESC—— 单列范围扫描 + 降序排序,在 B+ 树上逻辑冲突,大概率退化为全索引扫描 -
ORDER BY DATE(created_at) DESC—— 含函数表达式,任何降序索引都不可用,必须改用生成列
索引定义和ORDER BY必须逐列方向一致
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 DESC)仅对ORDER BY a DESC, b DESC有效;哪怕只写ORDER BY a DESC,也可能因缺少等值条件而无法利用 - 同一列不能在单个索引中重复声明方向,如
(a ASC, a DESC)会直接报错ERROR 1064
WHERE等值条件必须构成最左前缀
降序索引的排序能力不是天生的,它依赖前面的等值过滤来“固定”扫描起点。没有这个锚点,后续列的方向就失去意义。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
关键判断点:
- 索引是
(status, created_at DESC),那WHERE status = 'done'才能让created_at DESC参与排序;只写WHERE user_id = 123,这个索引根本不会被选中用于排序 -
WHERE status = 'done' AND priority > 5 ORDER BY created_at DESC——priority > 5是范围条件,中断了最左前缀连续性,created_at DESC依然失效 - NULL 值在
DESC索引中默认排最前(不是最后),如果业务逻辑依赖 NULL 排序位置,需额外验证
降序索引不是免费的,空间和兼容性都要算进去
物理上,DESC 索引是把键值做逆序编码存的(比如字符串按字节翻转),和对应 ASC 索引完全不共享存储结构。
容易被忽略的细节:
-
INDEX (id ASC)和INDEX (id DESC)必须各建一个,磁盘空间翻倍,写入维护开销也双份 - 主键中用
DESC要特别小心:8.0.11 及更早版本会静默忽略,8.0.12+ 才真正生效 - 覆盖查询(
SELECT id FROM t WHERE status = 1 ORDER BY id DESC)搭配INDEX(status, id DESC)最有价值;但一旦SELECT *且表含大字段,回表后结果无序,仍可能触发Using filesort
降序索引的核心价值不在“让 DESC 查询变快”,而在于**让特定组合的过滤 + 排序 + 覆盖三件事一次做完**。一旦 WHERE 条件松动、ORDER BY 方向错位、或 SELECT 列超出索引范围,它就退回原形——只是个普通索引,连排序都帮不上。










