调大sort_buffer_size反而让查询更卡,因其是每个连接独占内存,200并发设4MB即耗800MB,易触发ptmalloc争用致线程卡在malloc;真正需扩容的信号仅是Sort_merge_passes持续上升,而根本解法是建覆盖索引、精简字段、改游标分页。

为什么调大 sort_buffer_size 反而让查询更卡
MySQL 5.7 的 sort_buffer_size 是每个连接独占的内存,不是共享池。设成 4M 看似能撑住排序,但 200 个并发连接就吃掉 800MB,极易触发 ptmalloc 分配区争用,线程卡在内存申请上——你看到的“慢”,其实是卡在 malloc,不是卡在排序。
真正需要扩容的信号只有一个:SHOW STATUS LIKE 'Sort_merge_passes' 持续上升。这个值代表排序被迫落盘合并的次数;若每秒 > 5 次,才说明当前缓冲区确实不够用。
盲目调高该参数还掩盖了根本问题:索引没覆盖排序路径。它不会让 Using filesort 消失,只会让磁盘排序变成内存排序,代价是并发能力断崖下跌。
怎么判断是不是真缺 buffer,而不是索引没建对
先看 EXPLAIN 输出:
- 如果
Extra出现Using filesort,但rows很小(比如 - 如果
rows很大(>10 万)且Sort_merge_passes飙升,再考虑 buffer 调优; - 如果
Extra是Using temporary; Using filesort,优先检查是否GROUP BY + ORDER BY混用、或SELECT *带了大字段(如TEXT),这些会直接撑爆 buffer。
记住:buffer 不足只影响“怎么排”,而索引失效决定“要不要排”。前者是优化,后者是修复。
临时调高 buffer 比全局改更安全
全局修改 sort_buffer_size 对所有新连接生效,但已有连接不受影响,且容易引发内存震荡。更稳妥的做法是按需动态设置:
SET SESSION sort_buffer_size = 2097152; —— 仅对当前会话生效,配合具体慢查询使用;
SELECT /*+ SET_VAR(sort_buffer_size = 2097152) */ name FROM user ORDER BY age LIMIT 50; —— MySQL 5.7.7+ 支持 hint 方式,粒度更细;
用完立刻恢复:SET SESSION sort_buffer_size = DEFAULT;,避免后续语句误继承。
比调 buffer 更有效的三件事
绝大多数情况下,与其折腾 buffer,不如做这几件事:
- 给
WHERE + ORDER BY字段建联合索引,例如WHERE status = ? ORDER BY created_at DESC→ 建INDEX idx_status_created (status, created_at); - 避免
SELECT *,只查必要字段,减少单行体积,让同样 buffer 能塞更多行; - 对分页深度大的场景(如
LIMIT 10000, 20),改用游标分页,用上一次结果的created_at和id作为下一页条件,彻底绕过排序。
最易被忽略的一点:MySQL 5.7 的排序瓶颈,90% 不在内存大小,而在索引是否自然覆盖了 WHERE 条件、排序字段和 SELECT 列这三者组成的完整路径。











