应先确认explain中出现using filesort且sort_merge_passes持续上涨,再结合key_len小、含大字段或函数等条件判断sort_buffer_size是否为瓶颈;盲目调大会导致内存浪费和oom。

sort_buffer_size 不是 ORDER BY 变快的“开关”,它只在 MySQL 真的走 Using filesort 且内存不够时才起作用;盲目调大反而让并发线程更快吃光内存,甚至拖慢整个实例。
怎么确认当前 ORDER BY 确实依赖 sort_buffer_size?
先看执行计划是否真触发了文件排序:EXPLAIN SELECT * FROM t ORDER BY c,重点盯 Extra 列。只有出现 Using filesort,且同时满足以下任一条件,sort_buffer_size 才可能成为瓶颈:
-
type是index或range,但key_len很小(比如索引有 3 列,只用了前 1 列),说明索引没覆盖排序 - 排序字段是
TEXT、VARCHAR(2000)或用了函数(如ORDER BY UPPER(name)) -
SHOW GLOBAL STATUS LIKE 'Sort_merge_passes'持续上涨(比如每秒 > 0.5),这是磁盘归并的真实信号
为什么设成 4MB 后查询反而更慢?
sort_buffer_size 是每个连接独占分配的内存,不是按需动态伸缩的共享池。设成 4194304(4MB)后:
- 哪怕只排 10 行数据,MySQL 也会为这个连接一次性 mmap 分配 4MB,线程启动延迟明显
- 线上 100 个并发连接 × 4MB = 400MB 静态占用,但其中可能只有 3~5 个真在排序
- 旧版本(如 MySQL 5.7)直接 malloc 固定大小,容易导致内存碎片;容器或小内存机器上 OOM 风险陡增
- MySQL 8.0.22+ 已禁止
SET GLOBAL sort_buffer_size,改了配置也得重启,旧连接完全不受影响
安全有效的临时调参方法
别碰 my.cnf 全局配置——线上环境翻车率高。优先用会话级方式验证效果:
- 查当前值:
SELECT @@sort_buffer_size(注意单位是字节,不是 MB) - 对单条慢查询加 hint:
SELECT /*+ SET_VAR(sort_buffer_size = 2097152) */ id, name FROM users ORDER BY name LIMIT 1000 - 当前会话内生效:
SET SESSION sort_buffer_size = 2097152,仅影响后续语句 - ORM 用户注意:Django/Laravel 常在每次 query 前重置会话变量,必须在
cursor.execute()前显式SET SESSION
容易被忽略的协同参数:max_length_for_sort_data
sort_buffer_size 和 max_length_for_sort_data 共同决定排序模式。后者默认 1024 字节,意味着只要单行参与排序的数据(如 SELECT * + ORDER BY short_col)超过该值,MySQL 就自动切到 rowid 模式(只存排序字段+主键,再回表):
- 此时增大
sort_buffer_size效果极弱——因为瓶颈在回表 IO,不在内存缓冲 - 若排序字段短、但返回字段多,可同步调大
max_length_for_sort_data(如设为4096),让 full_columns 模式生效 - 必须一起压测:单独调
sort_buffer_size却卡在rowid模式,等于白忙
真正卡顿的大规模排序,根源往往不在缓冲区大小——而是缺覆盖索引、单行数据过大(如 JSON 字段含 Base64)、或 ORM 自动生成了 SELECT *。调参前,先跑一遍 EXPLAIN FORMAT=JSON,看清执行路径里到底哪一步在吃时间。











