MySQL 8.0.16+报错“The table is full”通常因TEMPTABLE引擎超temptable_max_ram(默认内存3%)或tmp_table_size与max_heap_table_size不一致导致临时表落盘至ibtmp1而非/tmp,需同步配置两组参数并优化SQL避免强制落盘。

查清到底是/tmp还是ibtmp1在撑爆磁盘
报错里带 /tmp/#sql-xxx 不代表真在 /tmp 写满——MySQL 8.0.16+ 默认启用 TEMPTABLE 引擎后,超内存的临时结果直接写进 ibtmp1,而不是 @@tmpdir。先执行:SELECT @@tmpdir; 和 SELECT @@datadir;,再分别跑 df -h /tmp 和 df -h /var/lib/mysql(或你查到的 datadir 路径)对比使用率。
如果 /tmp 没满但 /var/lib/mysql 接近 100%,且错误日志里有 ibtmp1 相关提示或 Created_tmp_disk_tables 增长但 lsof +D /tmp 找不到大文件,基本就是 ibtmp1 问题。
必须同步调 tmp_table_size 和 max_heap_table_size
这两个参数中较小的那个才生效,只改一个等于白改。比如设了 tmp_table_size = 512M,但 max_heap_table_size 还是默认 16MB,那临时表最多就用 16MB,一超就落盘。
实操建议:
- 在 /etc/my.cnf 的 [mysqld] 段里显式写两行:tmp_table_size = 256M 和 max_heap_table_size = 256M
- 重启前务必验证:SELECT @@tmp_table_size, @@max_heap_table_size;,两个值必须完全一致
- 单位别写错:256M 合法,256MB 会失效;2G 可以,旧版本不支持 G 单位的就写 2147483648
MySQL 8.0.16+ 必须配 temptable_max_ram
这个参数控制 TEMPTABLE 引擎总内存池上限,默认是物理内存的 3%(上限 4 GiB),不支持运行时修改,只认配置文件。
常见坑:
- 只调 tmp_table_size 却没动 temptable_max_ram,参数根本不起作用
- 错误地用 SET GLOBAL temptable_max_ram = 4294967296,会报错 Variable 'temptable_max_ram' is a read-only variable
正确做法:
- 查当前值:SELECT @@temptable_max_ram;(返回字节数)
- 在 [mysqld] 下加:temptable_max_ram = 4G(MySQL 8.0.23+ 支持 G,否则写数字)
- 物理内存 ≥128 GiB 时可设 6G,但别超 15%,否则可能触发系统 OOM
- 重启前执行:SET GLOBAL innodb_fast_shutdown = 0;,再 SHUTDOWN;,确保旧 ibtmp1 能被安全重建
SQL 写法比调参更关键
哪怕把所有内存参数拉满,以下写法照样强制走磁盘临时表:
- GROUP BY JSON_EXTRACT(col, '$.name') 或 ORDER BY UPPER(name):函数表达式无法用索引,优化器只能建临时表
- SELECT name, COUNT(*) FROM users GROUP BY dept_id:非聚合字段 name 出现在 SELECT 但没进 GROUP BY,MySQL 8.0+ 开了 ONLY_FULL_GROUP_BY 会直接报错,关了则行为不可控且必走临时表
- SELECT * 配合 ORDER BY created_at:只要表里有 TEXT 或超长 VARCHAR,哪怕只用几列,也会因字段类型限制被强制落盘
真正有效的解法:
- 对 GROUP BY 或 ORDER BY 字段建联合索引,覆盖所有参与列
- 必须用函数时,提前建生成列:ALTER TABLE t ADD COLUMN name_upper VARCHAR(100) GENERATED ALWAYS AS (UPPER(name)) STORED;,再对它建索引
实际处理时,最容易被忽略的是参数生效验证和引擎切换逻辑——tmp_table_size 和 max_heap_table_size 值不一致、temptable_max_ram 没写进配置、或者 SQL 里藏着隐式类型转换,都会让调参变成无用功。


















