应查sort_merge_passes是否每分钟涨几十上百,才是sort_buffer_size不足的铁证;若该值飙升而sort_scan/sort_range低,说明单次排序数据量大且缓冲区小,而非索引缺失。

直接看 Sort_merge_passes 是否每分钟涨几十上百——这才是 buffer 不足的铁证,不是 EXPLAIN 里有没有 Using filesort。
怎么确认真是 sort_buffer_size 不够,而不是索引没建对
只盯 EXPLAIN 的 Using filesort 容易误判:它只说明走了文件排序,不说明是 buffer 小还是根本没走索引。
- 执行
SHOW GLOBAL STATUS LIKE 'Sort_merge_passes';,如果该值每分钟上涨几十甚至上百,说明 MySQL 正反复切分、写盘、归并,才是sort_buffer_size不足的强信号 - 再对比
Sort_scan和Sort_range:若两者很低但Sort_merge_passes高,大概率是单次排序数据量大 + 缓冲区小;若三者都高,更可能是查询压根没走索引,得先加索引 - 用
EXPLAIN FORMAT=JSON查using_filesort节点里是否同时出现using_temporary,有则说明中间结果也撑爆内存了,不只是排序的事
为什么调大 sort_buffer_size 后还是报 Out of sort memory
常见错觉:以为 buffer 够大,排序就一定进内存。实际上有两个关键限制常被忽略:
-
max_length_for_sort_data默认是 1024,意思是单行参与排序的数据超 1KB 就强制切到 rowid 模式(只存排序字段+主键),实际进 buffer 的数据量很小——你SELECT *返回宽表,但ORDER BY只用两个INT字段,却因某个TEXT字段拉高单行体积,buffer 再大也没用 - MySQL 5.7 对大字段(如
TEXT、JSON)排序时,只要单行长度 >sort_buffer_size,哪怕只排 10 行,也会直接报Out of sort memory,而不是退化为磁盘归并 - 可临时试
SET SESSION max_length_for_sort_data = 4096;,配合sort_buffer_size一起压测,但注意它只对当前会话生效,且调高会增加内存占用
怎么安全地调 sort_buffer_size
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,跑完即释放
排查时最容易被忽略的一点
MySQL 5.7 的排序瓶颈,90% 不在内存大小,而在索引是否自然——比如 WHERE status = ? ORDER BY created_at DESC,必须建 INDEX(status, created_at),且字段顺序和方向要匹配;否则无论你怎么调 sort_buffer_size,都只是把“崩溃点”延后一点,反而更容易在并发时触碰系统总内存上限。











