bigywater.tech
MySQL / InnoDB · 45

降序索引

用一个两列排序方向不一样的ORDER BY,对比有没有方向匹配的降序复合索引,看MySQL 8.0的降序索引真实省下了什么。

2.5倍
方向匹配的降序索引带来的真实提速
0次Sort
从EXPLAIN里彻底消失的排序步骤

做的事:两列排序方向不一样的ORDER BY

customer_id升序、created_at降序,这种混合方向在业务里很常见(比如按客户分组、组内按时间倒序)。

代码SELECT customer_id, created_at FROM orders WHERE customer_id BETWEEN 1 AND 5000 ORDER BY customer_id ASC, created_at DESC;

验证:没有降序索引时,真实还是要单独排一次序

实测 · 本机EXPLAIN FORMAT=TREE输出(只有普通复合索引idx_cust)
-> Sort: orders.customer_id, orders.created_at DESC
    -> Index range scan on orders using idx_cust ...

虽然idx_cust已经按customer_id排好了序,但索引内部created_at这一列存的是升序,和查询要的降序方向不一致,MySQL没法直接利用索引顺序,只能先按索引取出数据,再单独做一次Sort。

验证:建一个方向完全匹配的复合索引,Sort真实消失了

代码CREATE INDEX idx_cust_created_desc ON orders(customer_id ASC, created_at DESC);
实测 · 本机EXPLAIN FORMAT=TREE输出
-> Filter: (orders.customer_id in (100,200,300))
    -> Covering index range scan on orders using idx_cust_created_desc ...   ← 没有Sort了

索引定义里显式声明了customer_id ASC, created_at DESC,这个方向组合和查询要的ORDER BY完全一致,MySQL真实可以直接按索引的物理存储顺序读出数据,Sort这一步真实从执行计划里消失了,直接变成Covering index range scan(顺带还是个覆盖索引,不用回表)。

验证:真实的耗时差距,1.5万行数据上约2.5倍

实测 · 本机执行耗时(同样返回1.5万行)
没有降序索引,走Sort:       64177 微秒 (约64.2毫秒)
有匹配方向的降序索引:       26036 微秒 (约26.0毫秒)   ← 快约2.5倍
MySQL从8.0开始真实支持在索引定义里给每一列单独指定ASC或DESC,这不是简单的语法糖——之前的版本里DESC关键字在索引定义里会被静默忽略,索引物理上永远是升序存储,遇到"一列升序、一列降序"这种混合排序需求,只能靠额外的Sort来补。真实建一个方向完全匹配的降序复合索引之后,这次查询在1.5万行数据上耗时从64.2毫秒降到26.0毫秒,快了约2.5倍,而且代价只是多维护一个索引,不需要改查询语句本身。

分步演示:混合方向ORDER BY,有没有降序索引的区别

每一步都是本文上面用EXPLAIN FORMAT=TREE和真实计时跑出来的原始结果。

方向匹配的降序索引,真实省掉了一次Sort
这一步新验证的索引

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),1.5万行orders子集(customer_id 1~5000)
    • CREATE INDEX ... (col1 ASC, col2 DESC):MySQL 8.0起真实支持的降序索引
    京ICP备2026067502号-1京公网安备11010502063487号