因为mysql 8.0.16+已彻底移除tmp_table_size参数,配置中保留会被静默忽略;真正生效的是max_heap_table_size(单表内存上限)和temptable_max_ram(temptable引擎总内存池,默认物理内存3%,超限直写ibtmp1),后者必须配置文件设置且不可动态修改。

为什么改了 tmp_table_size 还是爆磁盘?
MySQL 8.0.16+ 已彻底移除 tmp_table_size 参数,配置文件里留着它会被 mysqld 静默忽略,SELECT @@tmp_table_size 会报错或返回 0。真正起作用的是:max_heap_table_size 和 temptable_max_ram。前者控制单个内存临时表上限(也约束 MEMORY 表),后者才是 TEMPTABLE 引擎的总内存池——超了就直接往 ibtmp1 写,不走 /tmp,也不受 tmpdir 影响。
temptable_max_ram 必须写进配置且不能动态改
这个值决定所有 TEMPTABLE 临时表能用的总内存上限,默认是物理内存的 3%(上限 4 GiB),对报表类查询极容易卡死。它不支持 SET GLOBAL,必须在 /etc/my.cnf 的 [mysqld] 段中显式设置:
- 查当前值:
SELECT @@temptable_max_ram;(单位字节,比如2147483648= 2 GiB) - 推荐写法(兼容旧版本):
temptable_max_ram = 4294967296(即 4 GiB) - MySQL 8.0.23+ 可用单位:
temptable_max_ram = 4G - 若物理内存 ≥ 128 GiB,可设为
6G,但别超物理内存 15%,否则可能触发系统 OOM
必须同步调 max_heap_table_size 并限制 ibtmp1 增长
max_heap_table_size 不仅管显式创建的 MEMORY 表,也参与内存临时表阈值判定;而 ibtmp1 默认 autoextend 无上限,必须靠 innodb_temp_data_file_path 的 max 截断:
- 在配置中设为与
temptable_max_ram同量级(如都设 4G),但注意:它只取两者中较小者作为实际内存上限 - 加这一行:
innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:4G - 重启前务必执行:
SET GLOBAL innodb_fast_shutdown = 0;,再SHUTDOWN;,否则旧ibtmp1不会被重建 - 确认无长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;
怎么确认真是 ibtmp1 在撑爆磁盘?
别一看到 The table '/tmp/#sql-xxx' is full 就去查 /tmp——那是误导路径,真实落盘位置是数据目录下的 ibtmp1:
- 查 MySQL 实际 tmp 目录:
SELECT @@tmpdir;,然后df -h看对应挂载点是否真满 - 看状态变量:
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';和Created_tmp_tables,比值超 15% 就说明大量落盘 - 用
lsof +D /var/lib/mysql(替换为你的 datadir)看ibtmp1是否被 mysqld 持有且体积异常大 - 错误日志里出现
MY-012640: Error number 28或反复报The table is full,但/tmp和tmpdir都空,基本就是ibtmp1问题
最易被忽略的一点:参数只是兜底,SQL 本身才是根因。再大的 temptable_max_ram 也拦不住 GROUP BY DATE(created_at) 或 UNION 多字段无索引去重——得先用 EXPLAIN 看 Using temporary,再结合 performance_schema.events_statements_summary_by_digest 定位高消耗 SQL,否则调参等于给堵车路口加宽车道却不疏解车流。











