真正卡住内存的是temptable_max_ram——它默认仅1GB且不随其他参数自动扩容,MySQL 8.0.16+改用TempTable引擎后,该参数才决定内存池总量,超限直接分页写入ibtmp1而非tmpdir。

别急着调大 tmp_table_size,真正卡住内存的是 temptable_max_ram —— 它默认只有 1GB,且不随其他参数自动扩容。
为什么 GROUP BY / DISTINCT 突然变慢还频繁写磁盘?
MySQL 8.0.16+ 把内部临时表默认引擎从 MEMORY 换成 TempTable,但配套的内存池上限 temptable_max_ram 仍保持默认值(通常是总内存的 3%,上限 4 GiB)。它和 tmp_table_size 是两套独立控制逻辑:
-
tmp_table_size和max_heap_table_size取较小值,只作为单个临时表的“软上限”参考 -
temptable_max_ram才是TempTable引擎实际能用的内存池总量;超了就直接分页写入ibtmp1,不走tmpdir - 错误日志里出现
Writing temp table to disk或Using external sort,基本就是它在报警
怎么确认是不是 temptable_max_ram 不够?
连上库执行这两条命令,看输出是否匹配典型问题模式:
SELECT @@internal_tmp_mem_storage_engine;
SELECT @@temptable_max_ram, @@tmp_table_size, @@max_heap_table_size;
如果返回 TempTable 且 temptable_max_ram 是 1073741824(即 1GB),而另外两个参数设到了 2G 或更高,那几乎可以确定:内存池被卡死了。
- 别只查
tmp_table_size就以为“够大”——对TempTable来说,它不直接生效 - 监控
Created_tmp_disk_tables值飙升,但/tmp目录没写满?说明数据全进了ibtmp1
安全调高 temptable_max_ram 的实操要点
这个参数必须写进配置文件,不支持运行时修改。调整前注意:
- 在
my.cnf的[mysqld]段添加:temptable_max_ram = 4294967296(即 4G,推荐用字节值,避免单位缩写兼容性问题) - 值建议设为
tmp_table_size和max_heap_table_size中的较小值(比如都设 4G,这里也设 4G) - 机器内存 ≥ 128 GiB 时可设到 6G,但别超过物理内存的 15%,否则容器或系统可能 OOM
- 顺手配好
innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:10G,防ibtmp1无限膨胀 - 重启前执行
SET GLOBAL innodb_fast_shutdown = 0;再SHUTDOWN;,确保旧ibtmp1可被清理
为什么不能简单切回 MEMORY 引擎?
internal_tmp_mem_storage_engine = MEMORY 看似简单,但会暴露更多硬伤:
-
MEMORY不支持TEXT/BLOB/JSON字段,只要 SQL 里用了,立刻报ERROR 1163 (42000) - 行格式固定,
VARCHAR(200)当成CHAR(200)存,浪费内存,更容易触发落盘 - 无压缩、无内存池管理,高并发下容易抢锁、OOM,且超限不自动降级,直接崩
-
TempTable是 MySQL 8.0 的核心组件,深度集成在优化器中;禁用它会导致JSON_TABLE失效、EXPLAIN FORMAT=TREE异常、排序聚合性能陡降
真正要盯的不是引擎开关,而是 temptable_max_ram 是否与业务数据规模匹配,以及 SQL 本身是否在用表达式分组(如 UPPER(name))、是否缺失索引导致强制建临时表——这些才是压垮内存的真正源头。


















