直接调大sort_buffer_size无效,因它只控制order by排序,不干预group by临时表行为;真正需同步设置相等的tmp_table_size与max_heap_table_size,并优先建匹配索引、精简字段、分片查询。

直接调大 sort_buffer_size 不仅无效,还可能让磁盘更快写满——它根本不控制 GROUP BY 临时表行为。
为什么 GROUP BY 会疯狂写 /tmp/#sql_* 文件?
MySQL 执行 GROUP BY 时,先尝试用内存临时表(HEAP)存分组键和聚合值;一旦中间结果超限,就自动落盘到磁盘临时表(MyISAM 或 InnoDB),路径默认是 @@tmpdir(常为 /tmp)。这不是“慢”,是卡死:磁盘一满,所有 INSERT/UPDATE 都挂住,SHOW PROCESSLIST 里全是 Copying to tmp table 状态。
常见触发点:
-
GROUP BY字段无索引,或用了函数(如GROUP BY DATE(created_at)) - 结果行数巨大(分组数超千万,或单组数据达百万级)
-
SELECT *或含TEXT/BLOB列,单行体积爆炸 - 带
ORDER BY或LIMIT,强制多一轮排序
真正该改的两个参数必须完全相等
tmp_table_size 和 max_heap_table_size 共同决定内存临时表上限。MySQL 取二者中较小值作为实际阈值——只改一个等于白改。
检查当前值:SELECT @@tmp_table_size, @@max_heap_table_size;
安全调法(例如升到 512MB):SET GLOBAL tmp_table_size = 536870912;SET GLOBAL max_heap_table_size = 536870912;
永久生效:在 my.cnf 的 [mysqld] 段下加两行,重启或 reload:tmp_table_size = 512Mmax_heap_table_size = 512M
注意:
- 别设超过物理内存 20%~25%,并发高时极易 OOM
- 改完必须确认二者数值完全一致,否则仍按默认小值(常为 16MB)运行
比调参更关键的三件事
参数只是兜底。磁盘爆满本质是查询失控信号,优先做这些:
- 建联合索引,顺序严格匹配
GROUP BY字段:如GROUP BY status, created_at,就建INDEX(status, created_at);WHERE 条件字段必须放最左(WHERE reg_date > '2024-01-01' GROUP BY city→INDEX(reg_date, city)) - 删掉
SELECT *,只取分组字段和聚合字段;尤其避开TEXT、VARCHAR(2000)等大列参与分组 - 拆分大聚合:用主键范围分片,例如先
INSERT INTO tmp_ids SELECT id FROM t WHERE id BETWEEN 100000 AND 200000,再JOIN聚合;或按时间分段(WHERE created_at BETWEEN '2026-09-01' AND '2026-09-02')后UNION ALL
执行前务必跑 EXPLAIN,看 Extra 是否出现 Using temporary——只要出现,说明索引没被用于分组逻辑,调参毫无意义。
清理残留临时文件要小心
/tmp/#sql_* 文件在 mysqld 正常退出时自动删除,但崩溃或 kill -9 后会残留。手动清理前必须确认:
- 无活跃连接卡在
Copying to tmp table:mysql -e "SHOW PROCESSLIST;" | grep -E "(Copying|Sending)" - 无 mysqld 进程正占用:
lsof +D /tmp | grep mysqld - 只删修改时间早于当前小时的文件:
find /tmp -name "#sql_*" -mmin +60
别碰 ibdata1、ib_logfile* 或 ibtmp1(除非你明确知道怎么重建它)——误删会导致实例无法启动。










