调大tmp_table_size必须同步调整max_heap_table_size,因为mysql取二者较小值作为内存临时表上限,若不一致(如前者512mb、后者默认16mb),实际仍按16mb截断导致频繁落盘报错。

调大 tmp_table_size 不能单独生效,必须同步改 max_heap_table_size,否则事务照样中断。
为什么只改 tmp_table_size 没用
MySQL 实际使用的内存临时表上限,是 tmp_table_size 和 max_heap_table_size 两个值中的较小者。很多人在配置里只写了 tmp_table_size = 512M,却没碰 max_heap_table_size(默认常为 16MB 或 64MB),结果系统悄悄按 16MB 截断——哪怕语句中间结果有 200MB,也立刻落盘,触发 ERROR 1114 (HY000): The table 'sql_xxx' is full。
常见错误现象:
- 执行
SHOW VARIABLES LIKE 'tmp_table_size';显示 512M,但SHOW VARIABLES LIKE 'max_heap_table_size';还是 16777216(即 16MB) - 事务中含
GROUP BY或ORDER BY,一跑就卡住或报错,SHOW STATUS LIKE 'Created_tmp_disk_tables';持续上涨
怎么安全地扩容这两个参数
不是数值越大越好,得看物理内存余量和并发连接数。盲目设成 1G 可能导致系统 OOM —— 尤其当 max_connections 较高时,每个连接都可能独占一份该内存。
- 先确认当前实际使用压力:
SHOW GLOBAL STATUS LIKE 'Created_tmp%';算出Created_tmp_disk_tables / Created_tmp_tables比值,>10% 就说明已频繁落盘 - 在
/etc/my.cnf的[mysqld]段里显式写两行,值必须完全相等,例如:tmp_table_size = 256M<br>max_heap_table_size = 256M
- 重启前务必验证:
SELECT @@tmp_table_size, @@max_heap_table_size;,确保返回值一致且单位是字节(256M ≈ 268435456) - OLTP + 报表混跑的实例,建议上限 ≤ 物理内存的 20%,比如 32GB 机器最多设 6GB;若单条报表 SQL 中间结果预估超 300MB,宁可拆分查询,也不硬顶
改了参数还是报错?检查这三个地方
事务中断不一定是参数问题,更可能是 SQL 写法触发了强制落盘逻辑,再大的内存也拦不住。
-
ORDER BY UPPER(name)、GROUP BY JSON_EXTRACT(data, '$.id'):函数表达式无法走索引,优化器只能建磁盘临时表 -
SELECT *配合ORDER BY created_at:只要表里有TEXT或长VARCHAR字段,即使 SELECT 只要几列,也会因字段类型限制被强制落盘 - 临时表路径(
SELECT @@tmpdir;)所在分区满了,或 MySQL 进程无写权限(如挂载在/mnt/ssd/tmp但没chown mysql:mysql /mnt/ssd/tmp)
事务里临时表溢出的真实信号是什么
别等报错才反应。关键看执行计划和状态变量交叉验证:
- EXPLAIN 输出中出现
Using temporary; Using filesort,且type是ALL或key为NULL,说明索引没生效,大概率要落盘 - 慢日志里带
Copying to tmp table on disk的语句,就是罪魁祸首;开SET GLOBAL log_queries_not_using_indexes = ON;能快速抓出来 -
SHOW PROFILE FOR QUERY N;中 “Copying to tmp table on disk” 耗时占比 >30%,基本可以判定是它拖垮了事务响应
真正麻烦的是那些“看起来参数够、SQL 也规范,但就是偶尔中断”的场景——往往是因为某条报表语句中间结果大小波动剧烈,而内存参数是静态的。这时候光调参没用,得结合 EXPLAIN FORMAT=JSON 看 estimated_row_count × avg_row_length,再决定是加索引、改写子查询,还是切分数据。











