MySQL 8.0.12+倒序索引仅在ORDER BY方向、列顺序、WHERE等值条件及SELECT字段均与索引逐列严格对齐且全覆盖时,才能真正跳过filesort并避免回表;否则无效甚至更慢。

倒序索引 + 覆盖索引不是叠加就快,而是必须方向对齐、列全包含
单独建 INDEX (created_at DESC) 再加 SELECT *,基本等于白干。倒序索引只解决排序路径,覆盖索引只解决回表开销,两者要真正协同,得让 ORDER BY 方向和索引定义逐列一致,且 SELECT 的所有字段都落在索引里——缺一不可。
常见错误现象:EXPLAIN 显示 key 命中了索引,Extra 却还有 Using filesort 或 Using temporary,说明方向没对上;或者 type 是 index 但 rows 接近全表,说明没走有效过滤,只是扫索引树。
- 方向错一位(比如索引是
(status, created_at DESC),但查询写ORDER BY status, created_at ASC),整个排序能力归零 - SELECT 字段漏了一个不在索引里的列(比如索引含
status, created_at, user_id,但语句写了SELECT status, created_at, user_id, amount),立刻触发回表,结果集不再有序,MySQL 只能补filesort - WHERE 条件没压住最左前缀(比如索引是
(status, created_at DESC),但查询只有WHERE created_at > '2025-01-01'),索引连过滤都用不上,更别说排序
怎么写 CREATE INDEX 才真正生效
MySQL 8.0.12+ 才把 DESC 当物理方向存进 B+ 树;8.0.11 及更早版本会静默忽略,建出来还是升序索引。别信语法不报错就以为成了。
验证方式必须三步交叉确认:
- 执行
SHOW CREATE TABLE your_table,输出里必须明确出现created_at DESC—— 如果只写created_at,说明没落地 - 查系统表:
SELECT COLLATION FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 'your_table' AND INDEX_NAME = 'idx_name' AND SEQ_IN_INDEX = 1,返回D才代表首列是降序(A是升序) - 用
EXPLAIN FORMAT=TREE(MySQL 8.0.20+),看执行计划里有没有-> Sort节点;没有,且 Extra 不含Using filesort,才算真跳过排序
典型高效组合:等值过滤 + 倒序排序 + 全字段覆盖
真实业务分页几乎都带状态过滤,比如「查已完成订单,按更新时间倒序分页」。这种场景下,索引必须是联合的、方向显式的、字段完整的。
假设表结构为:orders(id, status, updated_at, user_id, amount),常用查询是:
SELECT id, user_id, amount FROM orders WHERE status = 'done' ORDER BY updated_at DESC LIMIT 20;
对应最优索引应为:
CREATE INDEX idx_status_updated_uid_amt ON orders (status, updated_at DESC, user_id, amount);
-
status放最左,满足等值过滤锚定 -
updated_at DESC紧跟其后,与 ORDER BY 方向严格一致 -
user_id, amount补在末尾,覆盖 SELECT 所有字段,避免回表 - 不写
id—— 主键自动包含在二级索引的叶子节点中(InnoDB),无需重复加
容易被忽略的性能断点:SELECT * 和 LIMIT 配合时的隐式陷阱
即使建了完美匹配的倒序覆盖索引,SELECT * 仍可能让优化器放弃它。因为回表取剩余字段后,数据顺序无法保证,MySQL 很可能判定「全表扫描 + filesort」反而更快。
更隐蔽的问题是 LIMIT 偏移量过大(比如 LIMIT 10000, 20)。这时即使索引生效,也要先跳过 10000 行再取,B+ 树仍需逐页遍历。这不是索引方向问题,而是分页模式本身缺陷。
- 优先改用游标分页(
WHERE updated_at ),彻底规避 offset 跳行 - 如果必须用 offset,确保 WHERE 条件足够强(如
status IN ('done', 'shipped')),让 rows 值远小于总行数,否则索引扫描成本仍高 - 定期用
sys.schema_unused_indexes清理长期未命中的倒序索引——它比升序索引多占约 10%~15% 空间,写入延迟也更高


















