MySQL ORDER BY 性能优化详解:Using filesort、联合索引、ASC/DESC 与覆盖索引

MySQL ORDER BY 性能优化详解:Using filesort、联合索引、ASC/DESC 与覆盖索引

  • [MySQL ORDER BY 性能优化详解:Using filesort、联合索引、ASC/DESC 与覆盖索引](#MySQL ORDER BY 性能优化详解:Using filesort、联合索引、ASC/DESC 与覆盖索引)
  • [一、先说结论:ORDER BY 应该怎么优化?](#一、先说结论:ORDER BY 应该怎么优化?)
  • [二、MySQL ORDER BY 为什么会慢?](#二、MySQL ORDER BY 为什么会慢?)
  • [三、什么是 Using filesort?](#三、什么是 Using filesort?)
    • [Using filesort 是不是一定会使用磁盘?](#Using filesort 是不是一定会使用磁盘?)
  • [四、如何避免 Using filesort?](#四、如何避免 Using filesort?)
  • [五、案例:没有索引时的 ORDER BY](#五、案例:没有索引时的 ORDER BY)
  • [六、建立 age + phone 联合索引](#六、建立 age + phone 联合索引)
  • [七、为什么 ORDER BY age 可以使用 age, phone 联合索引?](#七、为什么 ORDER BY age 可以使用 age, phone 联合索引?)
  • [八、多字段 ORDER BY 也要注意联合索引顺序](#八、多字段 ORDER BY 也要注意联合索引顺序)
  • [九、为什么 ORDER BY phone, age 不能直接使用 (age, phone) 排序?](#九、为什么 ORDER BY phone, age 不能直接使用 (age, phone) 排序?)
  • [十、ORDER BY 与联合索引的最左原则](#十、ORDER BY 与联合索引的最左原则)
  • [十一、两个字段全部 DESC,还能使用 ASC 索引吗?](#十一、两个字段全部 DESC,还能使用 ASC 索引吗?)
  • [十二、为什么 ASC + DESC 混合排序可能出现 filesort?](#十二、为什么 ASC + DESC 混合排序可能出现 filesort?)
  • [十三、如何优化 ASC + DESC 混合排序?](#十三、如何优化 ASC + DESC 混合排序?)
  • [十四、四种 ORDER BY 与索引的关系](#十四、四种 ORDER BY 与索引的关系)
  • [十五、一个非常重要的技术辨析:Using index ≠ ORDER BY 一定走索引排序](#十五、一个非常重要的技术辨析:Using index ≠ ORDER BY 一定走索引排序)
    • [1. Using index 的准确含义](#1. Using index 的准确含义)
    • [2. 判断 ORDER BY 是否额外排序,看什么?](#2. 判断 ORDER BY 是否额外排序,看什么?)
      • [没有 Using filesort](#没有 Using filesort)
      • [出现 Using filesort](#出现 Using filesort)
  • [十六、为什么 ORDER BY 优化建议尽量使用覆盖索引?](#十六、为什么 ORDER BY 优化建议尽量使用覆盖索引?)
  • [十七、ORDER BY 索引优化思维图](#十七、ORDER BY 索引优化思维图)
  • [十八、如果 Using filesort 无法避免怎么办?](#十八、如果 Using filesort 无法避免怎么办?)
  • [十九、sort_buffer_size 是什么?](#十九、sort_buffer_size 是什么?)
  • [二十、sort_buffer_size 是不是越大越好?](#二十、sort_buffer_size 是不是越大越好?)
  • 二十一、完整案例总结
    • [场景 1:没有索引](#场景 1:没有索引)
    • [场景 2:建立联合索引](#场景 2:建立联合索引)
    • [场景 3:全部降序](#场景 3:全部降序)
    • [场景 4:调换字段顺序](#场景 4:调换字段顺序)
    • [场景 5:一升一降](#场景 5:一升一降)
    • [场景 6:建立混合方向索引](#场景 6:建立混合方向索引)
  • [二十二、ORDER BY 性能优化的正确排查步骤](#二十二、ORDER BY 性能优化的正确排查步骤)
  • [二十三、ORDER BY 优化的 4 条核心原则](#二十三、ORDER BY 优化的 4 条核心原则)
    • [1. 根据排序字段建立合适索引](#1. 根据排序字段建立合适索引)
    • [2. 多字段排序注意索引字段顺序](#2. 多字段排序注意索引字段顺序)
    • [3. 尽量使用覆盖索引](#3. 尽量使用覆盖索引)
    • [4. ASC / DESC 混合时注意索引方向](#4. ASC / DESC 混合时注意索引方向)
  • [二十四、FAQ:MySQL ORDER BY 常见问题](#二十四、FAQ:MySQL ORDER BY 常见问题)
    • [1. MySQL ORDER BY 如何优化?](#1. MySQL ORDER BY 如何优化?)
    • [2. Using filesort 是什么意思?](#2. Using filesort 是什么意思?)
    • [3. Using filesort 一定很慢吗?](#3. Using filesort 一定很慢吗?)
    • [4. Using index 是什么意思?](#4. Using index 是什么意思?)
    • [5. Using index; Using filesort 能同时出现吗?](#5. Using index; Using filesort 能同时出现吗?)
    • [6. ORDER BY age DESC, phone DESC 能走 (age, phone) 索引吗?](#6. ORDER BY age DESC, phone DESC 能走 (age, phone) 索引吗?)
    • [7. 为什么 ORDER BY age ASC, phone DESC 可能不能使用普通联合索引排序?](#7. 为什么 ORDER BY age ASC, phone DESC 可能不能使用普通联合索引排序?)
    • [8. ORDER BY phone, age 能使用 (age, phone) 索引吗?](#8. ORDER BY phone, age 能使用 (age, phone) 索引吗?)
    • [9. sort_buffer_size 越大越好吗?](#9. sort_buffer_size 越大越好吗?)
  • [二十五、一张表总结 MySQL ORDER BY 优化](#二十五、一张表总结 MySQL ORDER BY 优化)
  • [二十六、30 秒记住 ORDER BY 优化](#二十六、30 秒记住 ORDER BY 优化)
  • 二十七、总结

MySQL ORDER BY 性能优化详解:Using filesort、联合索引、ASC/DESC 与覆盖索引

前言

在 MySQL 中,ORDER BY 是非常常见的排序操作。

例如:

sql 复制代码
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

SQL 本身很简单,但当数据量越来越大时,排序可能成为查询性能瓶颈。

MySQL 对 ORDER BY 的处理,大体可以分为两类:

  1. 利用索引本身的有序性返回结果
  2. 无法利用索引顺序,需要额外执行 filesort 排序

因此,优化 ORDER BY 的核心目标可以概括为一句话:

尽可能让 MySQL 利用索引本身的顺序完成排序,减少额外的 filesort。

本文围绕 MySQL ORDER BY 优化展开,重点讲清楚:

  • Using filesort 到底是什么意思
  • Using index 又是什么意思
  • 为什么联合索引可以优化排序
  • 为什么 ORDER BY age, phone 可以走索引,而 ORDER BY phone, age 可能不行
  • 为什么两个字段同时倒序仍然可以利用索引
  • 为什么一升一降可能再次出现 Using filesort
  • 如何创建 ASC / DESC 混合索引
  • 覆盖索引为什么有利于排序性能
  • 无法避免 filesort 时还能怎么优化

一、先说结论:ORDER BY 应该怎么优化?

如果只记本文最重要的内容,可以记住下面 4 条:

MySQL ORDER BY 优化原则:

  1. 根据排序字段建立合适的索引;
  2. 多字段排序时,注意联合索引的字段顺序;
  3. ASCDESC 混合排序时,让索引方向与查询排序方向匹配;
  4. 无法避免 filesort 时,再考虑 sort_buffer_size 等排序参数。

另外,查询字段能够被索引覆盖时,通常还可以减少回表,因此实际优化中也应尽量考虑覆盖索引


二、MySQL ORDER BY 为什么会慢?

假设执行:

sql 复制代码
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

如果 agephone 没有合适的索引,MySQL 无法直接按照索引顺序获得最终结果。

此时就需要:

text 复制代码
读取符合条件的数据
        ↓
放入排序过程
        ↓
按照 age、phone 排序
        ↓
返回最终结果

这会产生额外的排序成本。

EXPLAIN 执行计划中,通常可以通过:

text 复制代码
Using filesort

判断 MySQL 是否额外执行了排序。


三、什么是 Using filesort?

例如执行:

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

如果当前没有适合排序的索引,Extra 中可能出现:

text 复制代码
Using filesort

Using filesort 表示 MySQL 不能直接利用索引顺序得到最终排序结果,需要额外执行一次排序阶段 。MySQL 官方文档同样将 filesort 描述为无法通过索引满足 ORDER BY 时执行的额外排序过程。 (MySQL开发者专区)


Using filesort 是不是一定会使用磁盘?

不是。

这是一个很容易产生误解的地方。

虽然名字叫:

text 复制代码
filesort

但它并不意味着:

"只要出现 Using filesort,就一定会进行磁盘文件排序。"

MySQL 会使用排序缓冲区处理排序;当排序数据无法完全放入内存时,才可能使用临时磁盘文件。 (MySQL开发者专区)

因此更准确的理解是:

filesort 代表额外排序算法,而不是"必然磁盘排序"。


四、如何避免 Using filesort?

关键在于:

利用 B+Tree 索引本身已经有序的特点。

例如我们经常按照:

sql 复制代码
ORDER BY age, phone

进行排序,那么可以考虑建立联合索引:

sql 复制代码
CREATE INDEX idx_user_age_phone
ON tb_user(age, phone);

索引本身会按照:

text 复制代码
age
 ↓
相同 age 下继续按照 phone

维护有序结构。

于是查询:

sql 复制代码
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

就有机会直接按照索引顺序扫描,而不需要重新对结果进行额外排序。


五、案例:没有索引时的 ORDER BY

首先假设:

text 复制代码
age
phone

都没有适合当前排序的索引。

执行:

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age;

或者:

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

此时通常可能看到:

text 复制代码
Using filesort

因为 MySQL 无法借助适合的索引顺序直接返回结果。


六、建立 age + phone 联合索引

创建联合索引:

sql 复制代码
CREATE INDEX idx_user_age_phone_aa
ON tb_user(age, phone);

没有显式指定方向时,可以理解为:

sql 复制代码
age ASC,
phone ASC

即:

text 复制代码
(age ASC, phone ASC)

然后再次执行:

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

此时 MySQL 就具备了利用索引顺序完成排序的条件。


七、为什么 ORDER BY age 可以使用 age, phone 联合索引?

假设索引为:

text 复制代码
(age, phone)

那么 B+Tree 首先按照 age 排序,在相同 age 下再按照 phone 排序。

可以抽象成:

text 复制代码
age
│
├── 18
│   ├── phone A
│   ├── phone B
│   └── phone C
│
├── 20
│   ├── phone A
│   ├── phone B
│   └── phone C
│
└── 25
    ├── phone A
    ├── phone B
    └── phone C

因此:

sql 复制代码
ORDER BY age

可以利用索引前半部分的顺序。

同样:

sql 复制代码
ORDER BY age, phone

也与索引整体顺序匹配。


八、多字段 ORDER BY 也要注意联合索引顺序

现在索引是:

text 复制代码
(age, phone)

查询:

sql 复制代码
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

排序字段顺序与索引一致:

text 复制代码
索引:
age → phone

ORDER BY:
age → phone

这种情况可以很好地利用索引。

但是,如果改成:

sql 复制代码
SELECT id, age, phone
FROM tb_user
ORDER BY phone, age;

排序顺序变成:

text 复制代码
phone → age

而索引是:

text 复制代码
age → phone

两者的全局顺序并不一致。

因此很可能再次出现:

text 复制代码
Using filesort

九、为什么 ORDER BY phone, age 不能直接使用 (age, phone) 排序?

这是理解联合索引排序优化的关键。

对于:

text 复制代码
INDEX(age, phone)

索引真正保证的是:

text 复制代码
先按照 age 排序
age 相同时再按照 phone 排序

它并不能保证:

text 复制代码
所有 phone 在整个索引范围内都是全局有序的

举个例子:

text 复制代码
age = 18, phone = 135...
age = 18, phone = 139...
age = 20, phone = 131...
age = 20, phone = 138...

按照整个索引看,它首先满足:

text 复制代码
age 有序

而不是:

text 复制代码
phone 有序

因此:

sql 复制代码
ORDER BY phone, age

无法简单通过:

text 复制代码
(age, phone)

这个索引获得全局正确的排序结果。


十、ORDER BY 与联合索引的最左原则

对于联合索引:

text 复制代码
(age, phone)

以下查询比较容易利用索引顺序:

sql 复制代码
ORDER BY age;

以及:

sql 复制代码
ORDER BY age, phone;

而:

sql 复制代码
ORDER BY phone;

或者:

sql 复制代码
ORDER BY phone, age;

通常无法直接使用该联合索引满足完整的排序顺序。

因此可以总结为:

多字段 ORDER BY 优化时,应让排序字段顺序尽可能与联合索引字段顺序匹配。


十一、两个字段全部 DESC,还能使用 ASC 索引吗?

这是本节非常重要的一点。

假设索引:

sql 复制代码
CREATE INDEX idx_user_age_phone_aa
ON tb_user(age, phone);

其方向可以理解为:

text 复制代码
age ASC
phone ASC

现在执行:

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age DESC, phone DESC;

这种情况下,MySQL 可以通过反向扫描索引获得:

text 复制代码
age DESC
phone DESC

的结果。

MySQL 8.0 官方文档也明确说明,同方向多列排序时,可以通过对应索引或反向扫描索引满足 ORDER BY。 (MySQL开发者专区)

执行计划中可能看到类似:

text 复制代码
Backward index scan

表示正在反向扫描索引。


十二、为什么 ASC + DESC 混合排序可能出现 filesort?

现在索引依然是:

text 复制代码
(age ASC, phone ASC)

执行:

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age ASC, phone DESC;

排序要求变成:

text 复制代码
age   ASC
phone DESC

而原来的索引方向是:

text 复制代码
age   ASC
phone ASC

如果直接正向扫描:

text 复制代码
age   ASC  √
phone ASC  ×

如果整体反向扫描:

text 复制代码
age   DESC ×
phone DESC √

无论正向还是反向扫描,都无法同时满足:

text 复制代码
age ASC
phone DESC

因此就可能产生:

text 复制代码
Using filesort

十三、如何优化 ASC + DESC 混合排序?

如果业务中大量出现:

sql 复制代码
ORDER BY age ASC, phone DESC

可以建立与查询排序方向一致的联合索引:

sql 复制代码
CREATE INDEX idx_user_age_phone_ad
ON tb_user(age ASC, phone DESC);

此时索引的组织顺序就是:

text 复制代码
age   ASC
phone DESC

再次执行:

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age ASC, phone DESC;

MySQL 就具备通过索引顺序直接完成排序的条件。

MySQL 8.0 已经支持真正的降序索引,因此混合 ASC/DESC 的多列排序可以通过匹配方向的联合索引优化。 (MySQL开发者专区)


十四、四种 ORDER BY 与索引的关系

假设存在索引:

text 复制代码
(age ASC, phone ASC)

可以快速总结成下面这张表:

ORDER BY 是否容易利用该索引完成排序 原因
age ASC, phone ASC 与索引方向一致
age DESC, phone DESC 可以反向扫描
age ASC, phone DESC 两列方向不一致
age DESC, phone ASC 两列方向不一致

如果建立:

text 复制代码
(age ASC, phone DESC)

那么:

sql 复制代码
ORDER BY age ASC, phone DESC

就可以利用匹配的索引顺序。


十五、一个非常重要的技术辨析:Using index ≠ ORDER BY 一定走索引排序

很多教程会把:

text 复制代码
Using index

和:

text 复制代码
Using filesort

简单理解成:

text 复制代码
Using index    = 性能好
Using filesort = 性能差

作为入门记忆方法没有问题,但严格来说还需要进一步区分。

1. Using index 的准确含义

EXPLAIN Extra 中的:

text 复制代码
Using index

主要表示:

查询需要的列可以直接从索引中获得,不需要再读取完整的数据行。

也就是我们常说的:

覆盖索引(Covering Index)


2. 判断 ORDER BY 是否额外排序,看什么?

真正判断 ORDER BY 是否进行了额外排序,更重要的是观察:

text 复制代码
Using filesort

没有 Using filesort

意味着:

text 复制代码
没有额外执行 filesort 排序

出现 Using filesort

意味着:

text 复制代码
执行了额外排序阶段

MySQL 官方 EXPLAIN 文档也分别定义了 Using indexUsing filesort:前者指利用索引树取得查询所需列,后者则表示需要额外排序。 (MySQL开发者专区)

因此可能出现:

text 复制代码
Using index; Using filesort

这并不矛盾。

它表示:

text 复制代码
Using index
↓
查询列可以由覆盖索引取得

Using filesort
↓
但是 ORDER BY 仍然需要额外排序

这个区别在分析真实生产 SQL 时非常重要。


十六、为什么 ORDER BY 优化建议尽量使用覆盖索引?

例如查询:

sql 复制代码
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

如果索引能够覆盖查询需要的数据,就有机会:

text 复制代码
扫描索引
  ↓
得到排序结果
  ↓
直接取得需要的列

从而减少额外的数据页访问。

反之,如果查询:

sql 复制代码
SELECT *
FROM tb_user
ORDER BY age, phone;

即使排序字段有索引,也可能因为需要获取大量不在索引中的字段,增加回表访问成本。

因此:

ORDER BY 优化不仅需要考虑排序字段是否建立索引,还应该考虑查询字段能否合理地被索引覆盖。

但也不要为了覆盖所有查询而无限扩展索引,因为索引越多、越宽,会增加:

  • 存储空间;
  • INSERT 成本;
  • UPDATE 成本;
  • DELETE 成本;
  • 索引维护成本。

MySQL 官方同样强调,应在查询性能与索引维护成本之间取得平衡。 (MySQL开发者专区)


十七、ORDER BY 索引优化思维图

可以把整个判断流程记成:

text 复制代码
                    ORDER BY
                        │
                        ↓
              排序字段有没有合适索引?
                  │             │
                 否             是
                  │             │
                  ↓             ↓
             Using filesort   字段顺序匹配吗?
                                │
                        ┌───────┴───────┐
                        │               │
                       否               是
                        │               │
                        ↓               ↓
                 Using filesort    排序方向匹配吗?
                                        │
                                ┌───────┴───────┐
                                │               │
                               否               是
                                │               │
                                ↓               ↓
                    创建匹配 ASC/DESC     利用索引顺序
                          的联合索引

因此,ORDER BY 索引设计实际上主要需要看三个维度:

text 复制代码
字段
+
字段顺序
+
ASC / DESC 方向

十八、如果 Using filesort 无法避免怎么办?

并不是所有排序查询都值得建立专门索引。

例如:

text 复制代码
排序条件组合很多
临时统计查询很多
低频查询
排序字段变化非常频繁

如果为了所有 ORDER BY 都创建一个联合索引,会产生大量冗余索引。

因此某些场景下:

text 复制代码
Using filesort

是可以接受的。

当 filesort 无法通过 SQL 和索引设计消除,并且排序数据量比较大时,可以进一步关注:

text 复制代码
sort_buffer_size

十九、sort_buffer_size 是什么?

sort_buffer_size 是 MySQL 用于排序操作的缓冲区相关参数。

可以查看:

sql 复制代码
SHOW VARIABLES LIKE 'sort_buffer_size';

MySQL 8.0 官方文档给出的默认值为:

text 复制代码
262144 bytes

也就是:

text 复制代码
256 KB

如果大量排序无法通过索引优化,并出现较多排序合并操作,可以评估是否适当增大该参数。 (MySQL开发者专区6)


二十、sort_buffer_size 是不是越大越好?

不是。

不要简单地执行:

text 复制代码
排序慢
↓
把 sort_buffer_size 调到很大

因为该缓冲区与执行排序的会话相关。

如果全局设置过大,同时存在大量并发排序:

text 复制代码
单连接排序内存
×
并发连接数量

就可能产生比较大的内存压力。

MySQL 官方文档也建议不要盲目把该值全局设置得很大,更适合针对实际负载测试后调整。 (MySQL开发者专区6)

因此正确优化顺序应该是:

text 复制代码
① 优化 SQL
      ↓
② 优化索引
      ↓
③ 减少排序数据量
      ↓
④ 最后再考虑排序参数

而不是:

text 复制代码
一看到 Using filesort
↓
直接修改 sort_buffer_size

二十一、完整案例总结

假设:

text 复制代码
tb_user

包含:

text 复制代码
id
age
phone

场景 1:没有索引

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

可能出现:

text 复制代码
Using filesort

场景 2:建立联合索引

sql 复制代码
CREATE INDEX idx_user_age_phone_aa
ON tb_user(age, phone);

执行:

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age, phone;

此时具备利用索引顺序避免额外排序的条件。


场景 3:全部降序

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age DESC, phone DESC;

可以通过反向扫描:

text 复制代码
Backward index scan

利用相同的联合索引顺序。


场景 4:调换字段顺序

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY phone, age;

由于:

text 复制代码
索引:
age → phone

查询:
phone → age

顺序不匹配,因此可能:

text 复制代码
Using filesort

场景 5:一升一降

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age ASC, phone DESC;

现有索引:

text 复制代码
(age ASC, phone ASC)

无法完全满足排序方向,因此可能:

text 复制代码
Using filesort

场景 6:建立混合方向索引

sql 复制代码
CREATE INDEX idx_user_age_phone_ad
ON tb_user(age ASC, phone DESC);

然后:

sql 复制代码
EXPLAIN
SELECT id, age, phone
FROM tb_user
ORDER BY age ASC, phone DESC;

即可让索引方向与查询排序方向匹配。


二十二、ORDER BY 性能优化的正确排查步骤

线上遇到慢排序 SQL 时,可以按照以下顺序排查。

第一步:使用 EXPLAIN

sql 复制代码
EXPLAIN
SELECT ...
FROM ...
ORDER BY ...;

重点观察:

text 复制代码
key
rows
Extra

尤其是:

text 复制代码
Using filesort

第二步:检查排序字段

例如:

sql 复制代码
ORDER BY age, phone

确认是否存在:

text 复制代码
(age, phone)

这样的合适索引。


第三步:检查联合索引字段顺序

索引:

text 复制代码
(age, phone)

不等于:

text 复制代码
(phone, age)

因此不能看到字段都在索引中,就认为一定能优化排序。


第四步:检查 ASC / DESC

查询:

sql 复制代码
ORDER BY age ASC, phone DESC

需要重点检查索引方向是否与排序要求匹配。


第五步:检查能否使用覆盖索引

尽量避免不必要的:

sql 复制代码
SELECT *

只查询业务真正需要的列。


第六步:最后再考虑 sort_buffer_size

如果索引已经无法进一步优化,而且确实存在大量排序,再结合:

text 复制代码
Sort_merge_passes
并发连接数
单次排序数据量
服务器内存

评估排序缓冲区参数。


二十三、ORDER BY 优化的 4 条核心原则

最终可以总结成下面四条。

1. 根据排序字段建立合适索引

例如:

sql 复制代码
ORDER BY age, phone

可以考虑:

sql 复制代码
INDEX(age, phone)

2. 多字段排序注意索引字段顺序

索引:

text 复制代码
(age, phone)

更适合:

sql 复制代码
ORDER BY age, phone

而不是:

sql 复制代码
ORDER BY phone, age

3. 尽量使用覆盖索引

在满足业务需求的前提下,只查询真正需要的字段,尽量减少不必要的回表。


4. ASC / DESC 混合时注意索引方向

如果长期存在:

sql 复制代码
ORDER BY age ASC, phone DESC

可以考虑:

sql 复制代码
INDEX(age ASC, phone DESC)

二十四、FAQ:MySQL ORDER BY 常见问题

1. MySQL ORDER BY 如何优化?

最主要的方法是:

根据 ORDER BY 的字段、字段顺序和排序方向建立合适的联合索引,让 MySQL 尽可能利用索引顺序返回数据,从而避免额外的 filesort。

同时可以配合覆盖索引减少回表。


2. Using filesort 是什么意思?

Using filesort 表示 MySQL 无法完全通过索引顺序满足 ORDER BY,需要执行额外的排序过程。

它不代表一定进行了磁盘排序。


3. Using filesort 一定很慢吗?

不一定。

性能取决于:

  • 排序数据量;
  • 是否需要磁盘临时文件;
  • LIMIT 大小;
  • 内存;
  • 数据分布;
  • 查询并发量。

但是对于高频、大数据量查询,如果可以利用合适的索引消除 filesort,通常值得优化。


4. Using index 是什么意思?

Using index 主要表示:

查询所需要的数据可以从索引树中直接获得,也就是覆盖索引。

它并不等价于:

text 复制代码
ORDER BY 一定没有额外排序

判断排序是否进行了 filesort,应该重点查看:

text 复制代码
Using filesort

5. Using index; Using filesort 能同时出现吗?

可以。

例如:

text 复制代码
Using index; Using filesort

表示:

查询列能够通过覆盖索引获得,但是 ORDER BY 仍然需要额外排序。


6. ORDER BY age DESC, phone DESC 能走 (age, phone) 索引吗?

可以。

当索引为:

text 复制代码
(age ASC, phone ASC)

时,可以通过反向扫描得到:

text 复制代码
(age DESC, phone DESC)

7. 为什么 ORDER BY age ASC, phone DESC 可能不能使用普通联合索引排序?

因为普通:

text 复制代码
(age ASC, phone ASC)

索引无法通过简单的正向或反向扫描同时满足:

text 复制代码
age ASC
phone DESC

这种混合排序方向。

可以考虑建立:

sql 复制代码
INDEX(age ASC, phone DESC)

8. ORDER BY phone, age 能使用 (age, phone) 索引吗?

通常不能直接使用该索引完成完整排序。

因为:

text 复制代码
(age, phone)

保证的是:

text 复制代码
先 age
再 phone

而不是:

text 复制代码
先 phone
再 age

9. sort_buffer_size 越大越好吗?

不是。

应该优先优化:

text 复制代码
SQL
↓
索引
↓
查询数据量

确实无法避免大量 filesort 后,再针对实际负载评估 sort_buffer_size


二十五、一张表总结 MySQL ORDER BY 优化

问题 推荐方案
排序字段没有索引 为高频排序建立合适索引
多字段排序 建立联合索引
字段顺序不匹配 调整联合索引字段顺序
全部 ASC 正向扫描索引
全部 DESC 可反向扫描索引
ASC + DESC 混合 建立对应方向的混合索引
查询字段较少 考虑覆盖索引
出现 Using filesort 检查是否能通过索引消除
filesort 无法避免 再评估 sort_buffer_size
不确定是否优化成功 使用 EXPLAIN 验证

二十六、30 秒记住 ORDER BY 优化

最后把整节内容压缩成一段:

text 复制代码
MySQL ORDER BY 优化的核心是利用索引本身的有序性。

如果 ORDER BY 无法利用索引顺序,
EXPLAIN 中通常会看到 Using filesort。

对于多字段排序:
字段顺序要与联合索引匹配;
全部 ASC 或全部 DESC 可以利用同一索引正向/反向扫描;
ASC、DESC 混合时需要关注联合索引的排序方向。

同时尽量使用覆盖索引减少回表。

如果 filesort 无法通过 SQL 和索引设计避免,
再考虑 sort_buffer_size 等排序相关参数。

二十七、总结

MySQL ORDER BY 优化并不是简单地:

text 复制代码
给排序字段加一个索引

真正需要同时考虑:

text 复制代码
排序字段
+
联合索引顺序
+
ASC / DESC 方向
+
查询字段是否可以覆盖

最关键的判断方法还是:

sql 复制代码
EXPLAIN

重点关注:

text 复制代码
Using filesort

如果能够利用合适的 B+Tree 索引直接获得有序结果,就可以避免额外排序。

最终记住:

ORDER BY 优化的本质,就是尽量把"运行时重新排序"转变为"直接读取已经有序的索引"。

这也是处理 MySQL 排序性能问题时最重要的优化思路。


若有转载:请标明出处:https://blog.csdn.net/CharlesYuangc/article/details/164186003

相关推荐
OriginCoding1 小时前
用 AI Agent 协作完成一个 Android TOTP 应用:从需求边界到 v1.0.0
android·ai编程
杉氧3 小时前
React 灵魂:深入剖析 Hooks 与副作用(Side Effects)陷阱
android·前端·react native
又见情义4 小时前
RK3568 + RTL8211F 网络唤醒(WOL)功能适配全记录
android·网络·驱动开发
用户0934077735144 小时前
HarmonyOS WPS Open SDK 实践:registerApp 鉴权与就绪门禁
android·typescript·harmonyos
jike_20264 小时前
会议离线录音APP:无网络记录能力对比
android·智能手机·语音识别
always_TT5 小时前
【Python 字符串格式化:format() 方法】
android·开发语言·python
恋猫de小郭5 小时前
AI 时代,一个优化 Flutter 的重复代码工具 Deslop
android·前端·flutter
RisunJan6 小时前
Android 面试知识点整理
android·面试·职场和发展
脚踏实地,坚持不懈!6 小时前
Android ANR 内核底层全解析:从一次触摸到“应用无响应”
android·linux