确认真需要调sort_buffer_size需同时满足:EXPLAIN显示Using filesort,且Sort_merge_passes每秒涨>1;或排序字段为TEXT/JSON/超长VARCHAR、type为range/index但key_len过小、using_filesort伴随using_temporary;否则纯属浪费内存。

怎么确认真需要调 sort_buffer_size
看到 Using filesort 不代表就得调这个参数。它只说明排序没走索引,但是否落盘,得看 Sort_merge_passes 是否持续上涨——每秒涨 > 1 就是磁盘归并频繁的硬信号。
用 EXPLAIN 看到 Using filesort 同时满足以下任一条件,才值得考虑调:
- 排序字段是 TEXT、JSON 或超长 VARCHAR(2000),索引实际失效
- type 是 range 或 index,但 key_len 很小(比如复合索引只用了前 1 列)
- EXPLAIN FORMAT=JSON 里 using_filesort 节点同时出现 using_temporary
为什么设成 4MB 反而更慢
sort_buffer_size 是每个连接独占分配的,不是共享池。哪怕只排 10 行数据,MySQL 也会按配置值一次性预分配整块内存。
- 100 个并发 × 4MB = 400MB 静态占用,但其中可能只有 3~5 个线程真在排序
- 线程启动变慢、内存碎片增加、容器或小内存机器上 OOM 风险明显上升
- 旧版本 MySQL(如 5.7)对大 buffer 分配开销敏感,反而拖慢首次排序响应
默认值 262144(256KB)对多数 OLTP 查询已够用;万级行、单行体积 ≤ 1MB 时,可试 1048576~4194304(1MB~4MB)。
怎么安全地临时调参验证效果
别一上来就改 my.cnf 全局配置——线上环境容易翻车,且重启后未必生效(MySQL 8.0.22+ 不支持 SET GLOBAL 动态修改)。
推荐做法:
- 对单条报表 SQL 加 hint:SELECT /*+ SET_VAR(sort_buffer_size = 2097152) */ id, name FROM users ORDER BY name LIMIT 1000
- 或会话内先设再查:SET SESSION sort_buffer_size = 2097152,再执行查询
- 查当前值用:SELECT @@sort_buffer_size(注意不是 @@global.sort_buffer_size)
- 监控变化必须对比调参前后 30 秒内 Sort_merge_passes 的增量
ORM 场景(如 Django、Laravel)常在连接初始化时重置会话变量,需在 query 前显式 SET SESSION,不能依赖全局值。
容易被忽略的协同参数:max_length_for_sort_data
sort_buffer_size 再大,如果 MySQL 因单行参与排序的数据超限而切到 rowid 模式(只存排序字段 + 主键),那真正进缓冲区的数据量就很小,调大纯属浪费。
默认 max_length_for_sort_data = 1024(1KB),意味着只要 SELECT * 中某字段(如 TEXT)拉高单行体积,就会触发降级。这时要同步调:
- SET SESSION max_length_for_sort_data = 4096
- 并确保排序字段本身体积小(比如只 ORDER BY created_at, status,不带宽表字段)
注意:max_length_for_sort_data 调太高会增加内存压力,且不会减少回表次数——它只影响排序阶段的数据组织方式。


















