确认临时表导致磁盘爆满需三步:查select @@tmpdir;定位实际路径,df -h看对应挂载点使用率;查show global status like 'created_tmp_disk_tables'与created_tmp_tables比值是否超10%;用lsof +d /path排除非mysql进程干扰。

临时表空间溢出不是“调大 tmp_table_size 就能解决”的简单问题,它往往是内存参数、SQL写法、磁盘路径三者失配的综合结果。盲目调参可能让OOM更早到来,或把压力转嫁给根分区。
怎么确认真是临时表导致磁盘爆满
别一看到 The table is full 或 / 分区 100% 就改配置。先定位是不是 MySQL 的临时表在写盘:
- 查 MySQL 实际用的临时目录:
SELECT @@tmpdir;,然后df -h看那个挂载点是否真的满了(比如/tmp或/var/tmp) - 查状态变量:
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';和Created_tmp_tables,如果前者占后者比例 >10%,说明大量查询被迫落盘 - 用
lsof +D /path/to/tmpdir(替换为上一步查到的真实路径)看哪些 PID 正在往里面写大文件,排除日志、备份等非 MySQL 进程干扰
tmp_table_size 和 max_heap_table_size 必须设相等
这两个参数中较小的那个才起作用——MySQL 会取 min(tmp_table_size, max_heap_table_size) 作为单个临时表的内存上限。但很多人只调 tmp_table_size,忘了 max_heap_table_size 默认是 16MB,结果调了白调。
- 在
/etc/my.cnf的[mysqld]段里显式设成一样,例如:tmp_table_size = 256M和max_heap_table_size = 256M - 重启前务必执行
SHOW VARIABLES LIKE 'tmp_table_size';和SHOW VARIABLES LIKE 'max_heap_table_size';确认两者值一致且生效 - 混跑 OLTP + 报表的实例,建议按峰值报表 SQL 中间结果大小来定,宁高勿低,但别超物理内存的 20%(否则可能触发系统 OOM)
ORDER BY / GROUP BY 强制走磁盘临时表的典型写法
即使内存参数调得再大,只要 SQL 写法踩坑,照样生成磁盘临时表。最常见的是这三类:
-
GROUP BY JSON_EXTRACT(col, '$.name')或ORDER BY UPPER(name):函数表达式无法用索引加速,优化器只能建临时表再排序/分组 -
SELECT name, COUNT(*) FROM users GROUP BY dept_id:name既没聚合也没出现在GROUP BY中,MySQL 8.0+ 开ONLY_FULL_GROUP_BY会直接报错,关了则行为不可控且强制走临时表 -
SELECT *配合ORDER BY created_at:如果表里有TEXT或大VARCHAR字段,哪怕只用几列,也会因字段类型限制被强制落盘
实操建议:对 GROUP BY 或 ORDER BY 字段建联合索引,覆盖所有参与列;必须用函数时,提前存为生成列(GENERATED COLUMN)再建索引。
tmpdir 挂 SSD 或独立分区的硬约束
就算内存参数拉满,超大中间结果仍会落盘。这时把 tmpdir 指向高速存储确实有效,但容易忽略几个关键点:
- 目标路径必须 MySQL 进程可读写:
chown mysql:mysql /mnt/ssd/tmp,且 SELinux 或 AppArmor 规则要放行 -
tmpdir路径不能是符号链接(MySQL 5.7+ 会拒绝启动),也不能跨文件系统挂载(如从/dev/vda1mount 到/data/tmp后再软链过去) - 如果用的是
innodb_temp_data_file_path(InnoDB 内部临时表空间),它默认在datadir下,和tmpdir是两套机制,不能互相替代
最常被忽略的是:很多线上环境 tmpdir 仍指向 /tmp,而 /tmp 是根分区下的子目录——一旦临时表爆满,就直接拖垮整个系统,连 SSH 都可能登录不了。











