MySQL 8.0.12+ 才真正支持物理降序索引,建了 INDEX (created_at DESC) 仍出现 Using filesort,是因为未满足严格条件:必须验证索引真为降序(SHOW CREATE TABLE 或 COLLATION='D'),且需联合索引(如 INDEX (status, created_at DESC))配合等值 WHERE 和完全匹配的 ORDER BY,单列降序索引对带过滤的分页基本无效。

建了 INDEX (created_at DESC) 还是 Using filesort?先验真伪
MySQL 8.0.12+ 才真正把 DESC 写进物理索引;8.0.11 及更早版本会静默忽略,建出来仍是升序索引。验证方式只有两个:SHOW CREATE TABLE table_name 输出里必须出现 created_at DESC;或查系统表:SELECT COLLATION FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_NAME = 'your_table' AND INDEX_NAME = 'your_idx',结果得是 D(A 表示升序)。别信“建了就有效”,没验证等于白建。
WHERE status = ? ORDER BY created_at DESC 必须配 INDEX (status, created_at DESC)
单列 INDEX (created_at DESC) 对带过滤条件的分页基本无效——优化器无法跳过排序。真实业务中,倒序分页几乎都带等值过滤(如状态、用户ID),这时必须用联合索引,且顺序和方向要严丝合缝:
- 等值列放最左(如
status),排序列紧随其后(如created_at DESC) - 索引定义必须显式写
DESC,写成INDEX (status, created_at)就是升序,不生效 - 查询的
ORDER BY必须逐列与索引方向一致:ORDER BY status, created_at DESC可以,但ORDER BY created_at DESC单独用不行 - 如果
WHERE status IN ('done', 'pending'),MySQL 8.0.20+ 才较稳定启用该索引排序能力;低版本大概率仍触发Using filesort
EXPLAIN 里关键三处不看,等于没调优
别只扫一眼 key 字段是否命中索引名。真正生效的标志是这三项同时满足:
-
Extra字段不含Using filesort(最好还有Using index) -
type是ref或range,不是ALL或index -
rows值明显缩小(比如从百万级降到几千),说明索引真在过滤,不只是“被选中”
若看到 Backward index scan,那是 MySQL 5.7 的 fallback 行为,不是 8.0 降序索引生效的信号。
排序字段重复时,ORDER BY created_at DESC, id DESC 不是可选项
时间字段(如 created_at)天然存在大量重复值,仅靠它排序无法保证跨页一致性。第1页末尾和第2页开头可能因内部排序抖动把同秒数据来回“捞”。必须补唯一列(如自增 id)构成确定性排序:
- 写法:
ORDER BY created_at DESC, id DESC(假设id自增) - 对应索引:
INDEX (created_at DESC, id DESC),不能漏掉id的DESC - 若用游标分页,上一页最后一条的
(created_at, id)要完整传入下一页:WHERE created_at
漏掉唯一二级排序字段,再好的降序索引也压不住分页乱序问题。


















