临时表落盘是必然触发的连锁反应:内存不足时强制写入ibtmp1或/tmp,因tmp_table_size与max_heap_table_size取小值、8.0+改用temptable_max_ram、含BLOB/TEXT或函数的SQL不支持内存引擎,导致磁盘持续膨胀。

临时表落盘不是“偶尔”,而是“必然触发”的连锁反应
MySQL 临时表本身不危险,危险的是它在特定条件下必须写磁盘,且写入位置不受你控制。最典型路径是:内存不够 → 强制落盘 → 写进 ibtmp1(InnoDB 临时表空间)或系统 /tmp → 文件只增不减 → 磁盘撑爆。这不是配置漏了,而是几个关键阈值被悄悄绕过。
-
tmp_table_size和max_heap_table_size取较小值作为内存上限;只改一个等于没改(比如设了tmp_table_size = 512M,但max_heap_table_size还是默认 16MB,实际就是 16MB 触发落盘) - MySQL 8.0.16+ 彻底弃用
tmp_table_size,改用temptable_max_ram+max_heap_table_size;旧配置文件里留着它,mysqld 启动时直接静默忽略 -
innodb_temp_data_file_path只控制新实例启动时的初始大小和上限,对已膨胀的ibtmp1文件完全无效——删不掉、缩不了、重启前停不下来
哪些 SQL 会“主动申请”写满磁盘
不是所有 Using temporary 都一样。有些语句哪怕参数调到 2GB,照样秒落盘,因为 MEMORY 或 TEMPTABLE 引擎根本不支持这些字段或操作。
- 查询含
BLOB/TEXT字段(MEMORY 引擎不支持,强制走磁盘临时表) -
GROUP BY或ORDER BY用了函数,如UPPER(name)、DATE(created_at)、JSON_EXTRACT(data, '$.id') - 排序字段类型不一致(比如
VARCHAR列和INT常量比较,隐式转换导致无法用内存引擎) - 嵌套查询中带
ORDER BY(外层不继承,纯属白占内存,还增加 spill 风险) - 三表以上无索引 JOIN,尤其没
WHERE条件约束,容易产生笛卡尔积级中间结果
为什么 df -h 看不出问题,但磁盘已经满了
因为真正撑爆磁盘的 ibtmp1 文件就在 MySQL 数据目录下(/var/lib/mysql/ibtmp1),它不属于系统 /tmp,也不走 @@tmpdir。你查 df -h /tmp 是对的,但查错了地方。
- 先确认真实路径:
SELECT @@tmpdir;(查系统临时目录)和SELECT @@datadir;(查数据目录,ibtmp1就在这儿) - 再看对应挂载点:
df -h /var/lib/mysql,而不是/tmp - 用
lsof +D /var/lib/mysql | grep '#sql\|ibtmp'查正在被占用的临时文件句柄 -
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';持续飙升,但Created_tmp_tables很低,说明绝大多数临时表都落盘了
重启是唯一释放 ibtmp1 的方式,但不能随便重启
ibtmp1 文件在 MySQL 正常关闭时会被删除,重启后重建为初始 12MB。但这个过程有严格前置条件,跳过一步就可能启动失败或残留脏数据。
- 必须先执行:
SET GLOBAL innodb_fast_shutdown = 0;(确保 InnoDB 完成 purge 和 buffer merge) - 确认无长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60; - 手动刷脏页:
FLUSH TABLES; - 再执行
SHUTDOWN;(比systemctl restart更安全) - 启动前检查配置:确保
innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:5G已写入my.cnf
真正的难点不在“怎么释放”,而在于“怎么不让它再涨”。参数只是缓冲带,SQL 才是源头。一条没加索引的 GROUP BY,比十个调参动作更能决定磁盘命运。


















