跳转到内容

MySQL 排序机制


在日常开发中,ORDER BY 几乎是不可避免的操作。但有时候查询很快,有时候却慢到让人怀疑人生。后来查阅资料才发现,背后的关键在于 MySQL 排序的实现机制。这里简单整理一下我的理解,也算是给自己留个笔记。

MySQL 排序大概分两种:

  1. 索引排序:如果排序字段刚好命中索引,而且顺序一致,MySQL 就会利用索引的有序性直接输出结果,完全不需要额外排序。这个效率最高。

  2. 文件排序(filesort):如果没法用索引,那就得自己排了。这时候 MySQL 会在内存磁盘里开辟空间进行排序。

这种场景其实很好理解,索引本身就是 B+Tree 结构,天然有序。

比如表里有 (age, name) 的联合索引:

SELECT * FROM user ORDER BY age, name;

这时候 MySQL 直接按照索引扫描就能得到有序结果,几乎没有额外成本。

但如果你写成:

SELECT * FROM user ORDER BY name, age;

就会和索引的顺序不一致,只能乖乖进入 filesort。

当索引帮不上忙时,MySQL 会自己做排序。这就是所谓的 filesort,名字里虽然有“file”,但它不一定真的会用到磁盘,可能直接在内存里就搞定。

如果数据量不大,MySQL 会把数据放到 sort_buffer 里排序。这里有两个模式:

  • 双路排序
    由参数 max_length_for_sort_data 控制,默认是 4096。
    SELECT 的列比较大时,MySQL 会只把 排序字段 + 行 ID 放进 sort_buffer 排序,排完后再回表取完整数据。因为要多走一次表,所以效率低点。

  • 单路排序
    如果列不大,所有需要的字段都能放进 sort_buffer,那排序一次搞定,不需要回表。相比双路排序,效率更高。

一开始 MySQL 只有双路排序,后来才优化出了单路。

如果排序数据量 超过 sort_buffer_size,MySQL 会把数据写到临时文件,再通过归并排序完成最终排序。这个过程涉及大量磁盘 IO,性能最差。

所以实际优化时,能避免走磁盘排序就避免,必要时可以调大 sort_buffer_size

EXPLAIN 看执行计划:

  • 如果 Extra 里没有 Using filesort,说明走的是索引排序;

  • 出现 Using filesort,那就是 MySQL 自己排了,至于是内存还是磁盘,要结合数据量和参数配置来看。

MySQL 排序的逻辑其实不复杂,顺序大概是这样的:

  • 命中索引 → 直接用索引排序

  • 否则 → filesort

    • 数据量小 → 内存排序(单路 / 双路)

    • 数据量大 → 磁盘排序(归并)

所以优化思路也很直接:能用索引就用索引;如果避免不了 filesort,就尽量让它在内存里完成。