结论:90%的“排序内存溢出”实为缺失联合索引或大字段整行加载所致,非sort_buffer_size配置问题;真需调整时应会话级动态设置、上限不超4mb,并严格匹配where+order by字段顺序建索引。

直接说结论:别全局乱调 sort_buffer_size,90% 的“内存溢出”根本不是它的问题,而是索引缺失或大字段(如 JSON)被整行加载导致的。真要配,必须按连接场景分层设,且上限卡死在 4MB 以内。
先确认是不是真需要调这个参数
看到 Out of sort memory 错误,第一反应不应该是改配置,而是查执行计划和数据特征:
- 用
EXPLAIN看查询是否出现Using filesort—— 如果有,优先建联合索引,比如WHERE a = ? ORDER BY b就建(a, b)索引 - 检查 SELECT 列表里有没有大字段:
JSON、TEXT、超长VARCHAR;MySQL 8.0.20+ 会把它们整个塞进排序缓冲区,单行 2MB 就能干翻默认 256KB - 运行
SELECT AVG(LENGTH(json_field)), MAX(LENGTH(json_field)) FROM table;,如果MAX接近或超过当前sort_buffer_size值,那就是它了
怎么安全地临时加大(推荐做法)
生产环境主库绝不该在配置文件里全局拉高 sort_buffer_size。真正稳妥的做法是只在必要时、在会话级动态设置:
- 报表类查询(低频、高资源需求):连接后立刻执行
SET SESSION sort_buffer_size = 8388608;(8MB),跑完即释放,不影响其他连接 - ORM 框架要注意:Django、SQLAlchemy 等可能重置会话变量,得在执行前显式再设一次
- 中间件如 ProxySQL 可能拦截并覆盖
SET,需确认其变量透传策略 - MySQL 8.0.22+ 不允许
SET GLOBAL sort_buffer_size,只能靠配置文件 + 重启,但不建议这么做
配置文件里怎么写才不翻车
如果非得在 my.cnf 里设(例如专用从库跑分析任务),必须遵守三条铁律:
- 值控制在
1M~4M之间,绝不上8M;max_connections = 200时,4M × 200 = 800MB静态内存开销,还没算其他 per-connection 参数 - 同步检查
tmp_table_size和max_heap_table_size,它们和sort_buffer_size共同决定单次操作的内存上限,别让三者加起来吃掉 buffer pool - 务必验证是否被多处配置覆盖:比如
[mysqld]段写了,[client]段也写了,后者对服务端无效,容易误判
最容易被忽略的陷阱
很多人调完发现没效果,或者调了反而更慢,问题往往出在这些地方:
-
sort_buffer_size是 per-thread 独占内存,不是共享池——100 个并发排序,每个都要一份,不是共用一份 - 它和
innodb_sort_buffer_size完全无关,后者只影响CREATE INDEX或大批量INSERT,改它对ORDER BY查询毫无作用 - 即使你设成 4MB,只要排序字段上有函数(如
ORDER BY UPPER(name))或表达式,照样走filesort,缓冲区再大也白搭 - 监控必须看
Sort_merge_passes而不是Sort_scan;前者持续上升才说明真在落盘,后者只是表示“用了排序”,未必有问题
复杂点在于:同一套配置在 OLTP 主库和报表从库上意义完全不同。主库上一个 1MB 的 sort_buffer_size 可能已足够,但从库跑月结统计时,临时提到 4MB 并配合合适索引,才能避免磁盘归并拖垮整个集群。别用一个值包打天下。











