调大sort_buffer_size后查询仍慢,主因是order by无索引、索引未被选用、字段超长截断、select *触发单路排序或中间件重置会话值;应先查sort_merge_passes等指标确认是否真瓶颈。

为什么调大 sort_buffer_size 后查询还是慢
常见现象是改了配置、重启 MySQL,SHOW VARIABLES LIKE 'sort_buffer_size' 看值确实变大了,但某条 ORDER BY 查询依然慢,EXPLAIN 里还带 Using filesort。这不是参数没生效,而是根本没走到“靠 buffer 拼性能”这一步。
真正原因往往有这几个:
-
ORDER BY字段没索引,或索引没被选中(比如类型不匹配、函数包裹、隐式转换) - 排序字段长度超限:
VARCHAR(2000)即使有索引,也可能因max_sort_length截断退化为 filesort - 用了
SELECT *或大量非覆盖字段,触发单路排序(single-pass),导致sort_buffer_size实际要容纳整行数据,远超预期 - 中间件或 ORM 在连接建立后执行了
SET SESSION sort_buffer_size = 262144强制重置,覆盖了你设的全局值
怎么判断是不是真该调 sort_buffer_size
别一看到 Using filesort 就急着改 buffer。先确认它是否已成瓶颈:
- 查状态变量:
SHOW GLOBAL STATUS LIKE 'Sort_merge_passes';—— 如果这个值持续上升(比如每分钟涨几十次),说明频繁归并,内存不够用 - 算比例:
Sort_merge_passes / (Sort_scan + Sort_range)> 0.1,基本可断定当前 buffer 不足 - 看单条语句:用
EXPLAIN FORMAT=JSON查sort_mode,如果是<sort_key additional_fields></sort_key>,说明走的是单路排序,对 buffer 压力更大;如果是<sort_key rowid></sort_key>,则只存排序键+主键,更省空间 - 注意
Created_tmp_disk_tables是否同步飙升——如果它也涨,说明不止排序,连临时表都在落盘,可能tmp_table_size或max_heap_table_size也得一起看
sort_buffer_size 设多少才安全又有效
默认 256KB(即 262144)在 OLTP 场景下够用,但一碰万级行排序就容易溢出。关键不是“设多大”,而是“怎么设不翻车”:
- 优先会话级设置:
SET SESSION sort_buffer_size = 4194304;(4MB),只影响当前连接,避免全局放大并发内存压力 - 线上环境慎用全局配置;若必须改,建议从
2M起步,观察Sort_merge_passes下降趋势,再逐步试到4M,超过8M很少必要 - 计算内存上限:
sort_buffer_size × max_connections必须 物理内存 × 0.3;比如 64GB 机器,max_connections = 200,那单连接最多只能设96MB,但实际推荐压到4MB以内 - MySQL 8.0.22+ 不支持
SET GLOBAL sort_buffer_size,只能写进my.cnf并重启;改完记得验证:SELECT @@sort_buffer_size;
比调 sort_buffer_size 更有效的三件事
Buffer 是最后一道防线。多数情况下,优化排序不该从这里开始:
- 给
ORDER BY字段建联合索引,顺序严格匹配,比如ORDER BY status, created_at DESC就建INDEX(status, created_at DESC)(MySQL 8.0+ 支持降序索引) - 把
SELECT *换成明确字段列表,尤其避开TEXT、超长VARCHAR;能走覆盖索引就绝不回表 - 检查
WHERE条件是否能大幅缩小排序集,比如加时间范围:WHERE created_at > '2026-04-01',比硬排百万行高效得多
真正容易被忽略的是:即使索引存在,只要排序方向和索引定义不一致(如索引是 ASC,查询却 ORDER BY ... DESC),MySQL 7.x 及更早版本大概率弃用索引——这点在升级前必须验证。











