真正该盯Sort_merge_passes是否持续上涨,而非仅看EXPLAIN的Using filesort;它上涨说明频繁磁盘归并,才是sort_buffer_size不足的强信号,且需结合Sort_scan/Sort_range及using_temporary判断是否索引缺失或rowid模式导致。

调大 sort_buffer_size 不能直接解决“频繁磁盘排序”,反而可能让高并发下内存更快耗尽;真正该盯的是 Sort_merge_passes 是否持续上涨,以及是否本可通过索引避免排序。
怎么确认真是 sort_buffer_size 不够,而不是索引没建对
只看 EXPLAIN 输出里有没有 Using filesort 不够——它只说明走了文件排序,不说明是不是因为缓冲区小。关键要看运行时状态:
- 执行
SHOW GLOBAL STATUS LIKE 'Sort_merge_passes';,如果该值每分钟涨几十甚至上百,说明 MySQL 正在反复切分、写磁盘、再归并,这才是缓冲区不足的强信号 - 对比
Sort_scan和Sort_range:若两者数值低,但Sort_merge_passes高,大概率是单次排序数据量大 + 缓冲区小;若三者都高,更可能是查询本身没走索引,得先加索引 - 用
EXPLAIN FORMAT=JSON查using_filesort节点里是否同时出现using_temporary,有则说明不仅排序落盘,连中间结果也撑爆内存了
调 sort_buffer_size 的安全操作方式
它不是全局池子,而是每个连接独占一份。设错单位、范围或生效层级,基本等于白调:
- 单位是字节,
SET SESSION sort_buffer_size = 4194304;是 4MB,不是4M或4096K(MySQL 不识别后缀) - 全局配置(
my.cnf)改完必须重启 MySQL 才生效;会话级设置只影响当前连接,适合报表类 SQL 临时加大 - 别在 ORM 或中间件里依赖全局值——Django、ShardingSphere 等常在连接建立后重置会话变量,得在 SQL 前显式加
SELECT /*+ SET_VAR(sort_buffer_size = 4194304) */ ... - 线上 OLTP 主库建议保持默认 256KB~1MB;专用从库跑报表时,可会话级设到 4MB~8MB,跑完即释放
为什么调大了还是慢?可能卡在 max_length_for_sort_data
sort_buffer_size 再大,如果 MySQL 按 rowid 模式排序(只存排序字段+主键),那实际进缓冲区的数据量很小,调再大也没用。这时候得看另一个开关:
- 默认
max_length_for_sort_data = 1024,意思是单行参与排序的数据超 1KB 就强制切到rowid模式 - 如果你
SELECT *返回宽表,但ORDER BY只用两个 INT 字段,却因某 TEXT 字段拉高单行体积,就会白白浪费sort_buffer_size - 可试
SET SESSION max_length_for_sort_data = 4096;,配合sort_buffer_size一起压测;但注意:它也会增加内存占用,别单独拉太高
比调参更关键的三件事
绝大多数“磁盘排序”问题,根源不在缓冲区大小:
- 加联合索引覆盖
WHERE + ORDER BY,比如WHERE status = ? ORDER BY created_at DESC就建INDEX(status, created_at),直接消除Using filesort - 避免
SELECT *,尤其表里有 JSON、TEXT 字段时——哪怕只排 10 行,单条记录几 MB 也会瞬间打爆缓冲区 - 分页别用
LIMIT offset, size,OFFSET 越大,MySQL 越要先把前面所有行排出来再丢弃;换成游标分页(如WHERE created_at )
真正容易被忽略的点:sort_buffer_size 和 read_rnd_buffer_size 是两套逻辑,前者管排序,后者管排序后回表;看到 Using filesort 后还带 Using where,大概率是后者不够,而不是前者。


















