根本原因是MySQL 8.0.16+默认启用TEMPTABLE引擎后,超内存临时结果直写ibtmp1,而默认temptable_max_ram过小(物理内存3%)、不可动态修改,且旧版tmp_table_size参数已被移除并静默忽略,导致磁盘悄无声息被打满。

根本原因不是“临时表用多了”,而是 MySQL 8.0.16+ 默认启用 TEMPTABLE 引擎后,超内存的临时结果直接写进 ibtmp1,且默认内存池太小、参数不可动态调、旧配置被静默忽略——三者叠加导致磁盘悄无声息被打满。
为什么改了 tmp_table_size 完全没用?
MySQL 8.0 已彻底移除该参数。如果你从 5.7 升级上来,配置文件里还留着它,mysqld 启动时会静默忽略,不报错也不警告。SELECT @@tmp_table_size 会返回 0 或直接报错 Unknown system variable 'tmp_table_size'。
- 真正起作用的是
max_heap_table_size(控制 MEMORY 表和内存临时表上限)和internal_tmp_mem_storage_engine(默认为TempTable) -
temptable_max_ram才是 TEMPTABLE 引擎的总内存池上限,它不支持SET GLOBAL,必须写进配置文件 - 只调
max_heap_table_size而不设temptable_max_ram,等于给新引擎配了个旧阀门——压根打不开
为什么 ibtmp1 涨到上百 GB 却查不到 /tmp 写满?
因为 ibtmp1 是 InnoDB 自己管理的临时表空间文件,路径在数据目录下(如 /var/lib/mysql/ibtmp1),完全不走系统 tmpdir。错误日志里报的 The table '/tmp/#sql-xxx' is full 是误导性路径,真实落盘位置就是 ibtmp1。
-
lsof +D /tmp查不到大文件,df -h却显示根分区 100%,大概率就是ibtmp1在撑爆 -
innodb_temp_data_file_path参数只在启动时生效,对已膨胀的ibtmp1文件完全无效 - 旧文件不会自动收缩,必须配合
innodb_fast_shutdown = 0+ 完整重启才能清理并重建
为什么重启后 ibtmp1 还是很大?
重启前没做干净收尾,InnoDB 就不会删旧文件。常见卡点:
- 没执行
SET GLOBAL innodb_fast_shutdown = 0(默认是 2,跳过 purge 和 merge,残留元数据) - 存在长事务未结束:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60 - 没执行
FLUSH TABLES,脏页未刷完,shutdown 不完整 - Linux 下
lsof | grep ibtmp1还能看到 mysqld 占用,Windows 下tasklist | findstr "mysql"有残留进程
哪些 SQL 最容易把 ibtmp1 写爆?
关键不是语法本身,而是执行计划是否触发 Using temporary 且结果集超出内存阈值:
-
EXPLAIN显示Using temporary+Using filesort,且rows估算值巨大 -
GROUP BY字段无索引,或用了函数(如GROUP BY DATE(created_at)) -
DISTINCT多字段 + 含VARCHAR(2000)或TEXT列 -
UNION(非UNION ALL)去重,关联列无索引支撑
最危险的组合是:查询没走索引 + temptable_max_ram 又设得太小(比如默认 2 GiB),几条报表 SQL 就能把 ibtmp1 撑到几十 GB —— 而你可能还在 /tmp 里翻日志找不着北。


















