MySQL 排序机制
在日常开发中,ORDER BY 几乎是不可避免的操作。但有时候查询很快,有时候却慢到让人怀疑人生。后来查阅资料才发现,背后的关键在于 MySQL 排序的实现机制。这里简单整理一下我的理解,也算是给自己留个笔记。
1. 排序的两条路
Section titled “1. 排序的两条路”MySQL 排序大概分两种:
-
索引排序:如果排序字段刚好命中索引,而且顺序一致,MySQL 就会利用索引的有序性直接输出结果,完全不需要额外排序。这个效率最高。
-
文件排序(filesort):如果没法用索引,那就得自己排了。这时候 MySQL 会在内存或磁盘里开辟空间进行排序。
2. 索引排序
Section titled “2. 索引排序”这种场景其实很好理解,索引本身就是 B+Tree 结构,天然有序。
比如表里有 (age, name) 的联合索引:
SELECT * FROM user ORDER BY age, name;这时候 MySQL 直接按照索引扫描就能得到有序结果,几乎没有额外成本。
但如果你写成:
SELECT * FROM user ORDER BY name, age;就会和索引的顺序不一致,只能乖乖进入 filesort。
3. 文件排序
Section titled “3. 文件排序”当索引帮不上忙时,MySQL 会自己做排序。这就是所谓的 filesort,名字里虽然有“file”,但它不一定真的会用到磁盘,可能直接在内存里就搞定。
3.1. 内存排序
Section titled “3.1. 内存排序”如果数据量不大,MySQL 会把数据放到 sort_buffer 里排序。这里有两个模式:
-
双路排序
由参数max_length_for_sort_data控制,默认是 4096。
当SELECT的列比较大时,MySQL 会只把 排序字段 + 行 ID 放进sort_buffer排序,排完后再回表取完整数据。因为要多走一次表,所以效率低点。 -
单路排序
如果列不大,所有需要的字段都能放进sort_buffer,那排序一次搞定,不需要回表。相比双路排序,效率更高。
一开始 MySQL 只有双路排序,后来才优化出了单路。
3.2. 磁盘排序
Section titled “3.2. 磁盘排序”如果排序数据量 超过 sort_buffer_size,MySQL 会把数据写到临时文件,再通过归并排序完成最终排序。这个过程涉及大量磁盘 IO,性能最差。
所以实际优化时,能避免走磁盘排序就避免,必要时可以调大 sort_buffer_size。
4. 怎么判断到底走哪种排序?
Section titled “4. 怎么判断到底走哪种排序?”用 EXPLAIN 看执行计划:
-
如果
Extra里没有Using filesort,说明走的是索引排序; -
出现
Using filesort,那就是 MySQL 自己排了,至于是内存还是磁盘,要结合数据量和参数配置来看。
MySQL 排序的逻辑其实不复杂,顺序大概是这样的:
-
命中索引 → 直接用索引排序
-
否则 → filesort
-
数据量小 → 内存排序(单路 / 双路)
-
数据量大 → 磁盘排序(归并)
-
所以优化思路也很直接:能用索引就用索引;如果避免不了 filesort,就尽量让它在内存里完成。