bigywater.tech
MySQL / InnoDB · 42

Filesort 到底是不是磁盘排序

同一个ORDER BY查询,只改sort_buffer_size,用Sort_merge_passes这个状态变量验证filesort到底什么时候会真的落盘。

6次→0次
sort_buffer_size从32KB调到64MB,归并次数的真实变化
110ms→75ms
对应耗时的真实变化,默认256KB已经接近最优

做的事:同一个ORDER BY,只改sort_buffer_size

5万行orders按amount降序排序,不走任何索引(amount上没有可用的排序索引时的路径),观察是否触发磁盘归并。

代码SET SESSION sort_buffer_size = <不同的值>; FLUSH STATUS; CREATE TEMPORARY TABLE sink AS SELECT customer_id, amount FROM orders ORDER BY amount DESC; SHOW SESSION STATUS LIKE 'Sort_merge_passes';

验证:sort_buffer_size不同,Sort_merge_passes真实不同

实测 · 本机执行输出
sort_buffer_size =    32768 (32KB):   Sort_merge_passes = 6   耗时约110.3ms
sort_buffer_size =   262144 (256KB,默认): Sort_merge_passes = 1   耗时约 77.0ms
sort_buffer_size = 67108864 (64MB):   Sort_merge_passes = 0   耗时约 75.2ms

Sort_merge_passes这个状态变量真实记录了排序过程中做了几次"外部归并"——当要排序的数据一次性装不进sort_buffer_size这块内存缓冲区时,MySQL会把数据分成若干块,每块在内存里排好序后写成一个临时文件,最后再对这些临时文件做归并,这个归并次数就是Sort_merge_passes。32KB缓冲区太小,5万行数据被切成很多小块,真实产生了6次归并;256KB(MySQL的默认值)也真实不够装下全部5万行,产生了1次归并;只有开到64MB,全部数据一次性能在内存里排完,归并次数才真实降到0。

结论:filesort这个名字,只有装不下的时候才名副其实

从32KB到256KB,耗时从110.3ms降到77.0ms,降了约30%;但从256KB(默认值,1次归并)再涨到64MB(0次归并),耗时几乎没变(77.0ms → 75.2ms)——真实说明MySQL默认的256KB缓冲区,对这个5万行的排序来说已经"够用"到只差临门一脚,多出来的那一次归并代价很小。filesort这个名字容易让人误以为"只要EXPLAIN里出现Using filesort,就一定在磁盘上排序",但真实的机制是:MySQL先尝试把要排序的数据整个放进sort_buffer_size指定大小的内存缓冲区里排序,只有装不下时才会真实分批写临时文件到磁盘再归并——"filesort"这个词描述的是这套排序算法的名字,不代表它必然发生在磁盘上。判断一次排序有没有真实落盘,看Sort_merge_passes这个状态变量的值,比看EXPLAIN里"Using filesort"这几个字更准确。

分步演示:三种sort_buffer_size下的归并次数

每一步都是本文上面用SHOW STATUS跑出来的原始结果。

Sort_merge_passes:判断filesort是否真的落盘
这一步新验证的缓冲区大小

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),5万行orders按amount降序排序
    • SESSION sort_buffer_size + SHOW STATUS LIKE 'Sort_merge_passes':判断排序是否真实触发磁盘归并
    京ICP备2026067502号-1京公网安备11010502063487号