确认sort_buffer_size不足需三步:先用explain format=tree查是否using filesort且type为all/index;再看sort_merge_passes是否持续上涨;最后测试limit 100是否仍报错,否则多为索引失效或字段类型问题而非缓冲区小。

不是内存不够,是数据库被迫把不该进内存的数据全塞了进去——核心要让排序和JOIN在索引里完成,而不是靠调大缓冲区硬扛。
怎么确认真是 sort_buffer_size 不足?
别一看到 Copying to tmp table on disk 就去改配置。先看执行计划是否真走了索引:
- 用
EXPLAIN FORMAT=TREE查ORDER BY或GROUP BY是否触发Using filesort;如果type是ALL或index,说明没走有效索引,调 buffer 没用 - 查
SHOW STATUS LIKE 'Sort_merge_passes':值持续上涨,才表明排序频繁落盘 - 跑小结果集测试:加
LIMIT 100后还报错?那大概率是字段类型或函数导致无法用索引,不是 buffer 小
为什么建了索引还是落盘?
索引建了≠能用上。常见卡点在字段类型和表达式:
-
WHERE status = '1'对 INT 字段查询 → 隐式转换 → 索引失效 → 全表扫描 → 排序数据量爆炸 -
ORDER BY UPPER(name)或ORDER BY DATE(created_at)→ 函数包裹 → 索引跳过 - 联合索引顺序错:比如
WHERE a = ? AND b = ? ORDER BY c,但索引是(c, a, b),最左前缀不匹配,排序仍要回表再排
work_mem / sort_buffer_size 到底设多大?
设太大浪费内存,太小白调。关键看“谁在用”和“用几次”:
-
work_mem(PostgreSQL)是每个哈希/排序操作独占的,不是整个查询。一个查询含 2 个Hash Join+ 1 个Sort,实际最多吃掉 3 ×work_mem -
sort_buffer_size(MySQL)是每个连接独占,高并发下设成 4MB 已足够,设 64MB 可能直接触发 OOM killer - 安全做法:会话级临时调:
SET SESSION sort_buffer_size = 4194304(4MB),或SET LOCAL work_mem = '8MB',验证后再决定是否写进配置
真正省内存的三件事,比调参管用
调参只是兜底,根治得从 SQL 和索引结构下手:
- 为
WHERE + ORDER BY组合建联合索引,字段顺序必须是“过滤列在前、排序列在后”,例如WHERE deleted = 0 ORDER BY created_at DESC→ 建INDEX idx_del_created (deleted, created_at) - 避免 SELECT *:宽表 + 大字段(
TEXT、VARCHAR(2000))会让 MySQL 直接放弃内存排序,强制走磁盘临时表 - 分批处理代替单次大 JOIN:用主键范围(
BETWEEN)或 IN 列表(≤1000 个 ID)拆查询,右表每次只加载需关联的行,彻底避开全表进内存
最容易被忽略的是:tmp_table_size 和 max_heap_table_size 必须同时调大才生效,且二者取较小值作为上限——只改一个,等于没改。










