MySQL 8.0.16+升级后临时表易落盘,主因是默认启用TEMPTABLE引擎并受temptable_max_ram(默认总内存3%)限制,超限直接写入ibtmp1而非tmpdir,导致磁盘爆满却查不到/tmp写入痕迹。

MySQL 8.0 升级后临时表突然爆磁盘,大概率不是 tmp_table_size 没调对,而是 temptable_max_ram 被默认值卡死了。
为什么升级后临时表更容易落盘?
MySQL 8.0.16+ 默认内部临时表引擎从 MEMORY 切换为 TEMPTABLE,它不再只看 tmp_table_size 和 max_heap_table_size,而是引入了独立的内存池控制参数:temptable_max_ram。这个值决定 TEMPTABLE 引擎最多能用多少内存做“内存阶段”,超了就直接写磁盘(InnoDB 临时表空间 ibtmp1),不经过 tmpdir。
它的默认值是总内存的 3%,上限 4 GiB——对报表类查询或大分组操作来说,往往不够。比如你有 64 GiB 内存,temptable_max_ram 默认约 2 GiB;但一个带多层 JSON 解析和窗口函数的 GROUP BY,中间结果轻松突破 3 GiB,立刻触发 ibtmp1 膨胀,甚至报错 The table '/tmp/#sql...' is full(注意:这个路径名是误导,实际写的是 ibtmp1)。
常见误判点:
- 只调
tmp_table_size = 512M,却没改temptable_max_ram,结果无效 - 看到
Created_tmp_disk_tables飙升,去查/tmp目录,发现没写满——其实数据全进了ibtmp1 - 误以为
internal_tmp_mem_storage_engine = TEMPTABLE就万事大吉,忽略了它仍需显式配额
怎么安全调高 temptable_max_ram?
必须在配置文件中设置,不支持运行时 SET GLOBAL。修改前确认当前值:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
SELECT @@temptable_max_ram;
推荐做法:
- 在
my.cnf的[mysqld]段添加:temptable_max_ram = 4G(不要写成4096M,MySQL 8.0.23+ 才完全支持单位缩写) - 若机器内存 ≥ 128 GiB,可设为
6G,但别超过物理内存的 15%,否则可能触发系统 OOM - 搭配
innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:10G使用,避免ibtmp1无限制增长 - 重启前执行
SET GLOBAL innodb_fast_shutdown = 0;,再SHUTDOWN;,确保旧ibtmp1可被删除
调完还是爆 ibtmp1?先查是不是 SQL 本身有问题
temptable_max_ram 是兜底,不是万能药。频繁触发说明查询逻辑存在硬伤:
-
EXPLAIN出现Using temporary且没走索引 → 检查GROUP BY/ORDER BY字段是否覆盖索引最左前缀 - 用
JSON_EXTRACT(col, '$.field')或UPPER(name)做分组 → 这类表达式无法走索引,强制建临时表 - SELECT * + ORDER BY time → 含 TEXT/VARCHAR(2000) 字段时,TEMPTABLE 会因行格式限制直接落盘,哪怕只选 2 列
- 子查询里写
ORDER BY ... LIMIT→ MySQL 8.0.23 前不优化物化,容易生成巨大中间集
真正有效的优化顺序是:改 SQL(加索引、拆 CTE、过滤前置)→ 调 temptable_max_ram → 最后才考虑增大 ibtmp1 上限。
监控和验证是否生效
重启后不能只看状态变量是否“变大”,要盯住真实行为:
- 查
SHOW GLOBAL STATUS LIKE 'Created_tmp%';,关注Created_tmp_disk_tables是否下降(理想比值 ≤ 5%) - 用
lsof -p $(pgrep mysqld) | grep ibtmp1看ibtmp1文件是否还在持续增长 - 执行典型慢查询,观察
SHOW PROCESSLIST状态是否还长时间卡在Creating tmp table - 检查错误日志是否有
MY-012926: Unable to create the shared innodb_temporary—— 这说明temptable_max_ram已到顶且ibtmp1也满了
最关键的细节:TEMPTABLE 的内存使用是“弹性池”,不是每个临时表独占配额。多个并发查询共享这个池,所以单次调大未必立竿见影,得结合并发压力一起看。

















