必须将tmp_table_size与max_heap_table_size设为相等值(如均为128m),因mysql取二者较小值作为内存临时表硬性上限,不一致会导致意外落盘、created_tmp_disk_tables激增及oom风险。

只调 tmp_table_size 基本无效,必须让它和 max_heap_table_size 完全相等,否则 MySQL 永远按两者中较小值来卡内存上限。
为什么改了 tmp_table_size 却没效果?
MySQL 创建内部临时表时,并不只看 tmp_table_size。它同时检查 tmp_table_size 和 max_heap_table_size,取其中更小的那个作为实际内存上限。线上常见配置是把 tmp_table_size 改成 256M,但 max_heap_table_size 还卡在默认 16M——结果所有查询仍被截断在 16M,Created_tmp_disk_tables 照样飙升。
典型错误现象包括:
-
SHOW PROCESSLIST中长期显示Creating tmp table状态 - 慢查询日志里频繁出现
Copying to tmp table -
ERROR 1114 (HY000): The table is full报错(尤其在GROUP BY或UNION场景)
怎么安全地设对两个参数?
必须同步设置,且优先走配置文件而非仅靠 SET GLOBAL。因为 SET GLOBAL 不影响已建立的连接,而生产环境连接池往往复用长连接,旧值会持续生效。
- 在
/etc/my.cnf的[mysqld]段显式写死两者:tmp_table_size = 128M<br>max_heap_table_size = 128M
- 重启后立即验证:
SELECT @@tmp_table_size, @@max_heap_table_size;,确保返回值完全一致 - 如果用 RDS(如阿里云),注意
loose_rds_max_tmp_disk_space限制(默认 10GB),即使内存够,磁盘临时表总量超限也会报错 - 单次调大不宜超过物理内存的 10%~15%,例如 64G 机器建议从 128M 起步;并发连接数高时,要估算总内存占用:
Threads_connected × tmp_table_size
调完还是落盘?先排除强制磁盘路径
参数设对了但 Created_tmp_disk_tables 不降,大概率不是内存不够,而是 SQL 本身触发了“强制落盘”逻辑——这时再调参毫无意义。
- 只要 SELECT 列里含
TEXT、BLOB或定义过宽的VARCHAR(2000),MySQL 就直接跳过内存表,哪怕你设成 1G -
GROUP BY JSON_EXTRACT(col, '$.name')、ORDER BY UPPER(name)这类带函数的字段,无法用索引加速,也强制走磁盘 - 执行计划里出现
Using temporary但没配对应索引,或UNION各子查询列类型不一致(触发隐式转换),都会绕过内存路径 - 检查
SELECT @@tmpdir;对应磁盘分区是否真有空间,ibtmp1文件可能已占满且不会自动收缩
比调参更值得优先做的三件事
参数只是兜底,真正省掉临时表得从 SQL 和索引入手:
- 给
GROUP BY或ORDER BY字段建联合索引,让 MySQL 直接流式分组/排序,跳过临时表 - 避免
SELECT *,只查真正需要的字段;多一个没用的TEXT字段就足以让整个中间结果强制落盘 - 嵌套查询优先改写为
JOIN,尤其当子查询条件能和外层关联时;IN (SELECT ...)在无索引场景下极易物化成大临时表
最常被忽略的一点:tmp_table_size 是 SESSION 级变量,连接池复用连接时,SET SESSION 只对当前会话有效,下次获取连接又回到全局默认值——所以配置文件固化才是生产环境唯一可靠方式。











