MySQL内存临时表上限取tmp_table_size与max_heap_table_size的较小值;任一超限即强制转磁盘临时表,且TEXT/BLOB或字段定义超512字节时直接跳过内存路径。

tmp_table_size 和 max_heap_table_size 中较小值被突破
MySQL 创建内存临时表时,并不是看单个参数,而是取 tmp_table_size 与 max_heap_table_size 的较小值作为上限。只要中间结果(比如 GROUP BY 后的分组键 + 聚合值、ORDER BY 排序行)的估算体积超过这个值,立刻转为磁盘临时表——不是“可能”,是确定行为。
常见误操作:
- 只调大
tmp_table_size,但max_heap_table_size仍卡在默认 16MB,实际阈值没变 - 高并发场景下每个连接独占一份该内存,总开销翻倍,实际可用远低于配置值
- 字段定义长度参与内存估算:哪怕 VARCHAR(2000) 实际只存 10 字符,也会按 2000 字节计入,极易提前触发落盘
遇到 TEXT/BLOB 或超宽字段时直接强制走磁盘
只要 SELECT 列、GROUP BY 列、DISTINCT 列或 UNION 的任意一列含 TEXT、BLOB 类型,或定义长度 > 512 字节,MySQL 就跳过内存临时表,直接创建磁盘临时表(InnoDB 引擎)。
这不是“数据量大了才落盘”,而是设计层面的硬限制:
-
SELECT *, COUNT(*) FROM logs GROUP BY level——*拖入大字段,必落盘 -
GROUP BY SUBSTRING(content, 1, 1000)—— 表达式推导出长度 > 512,不看实际内容 -
UNION中任一列定义超限,整个 UNION 结果集强制磁盘临时表
显式指定 ENGINE=InnoDB 或使用 CREATE TEMPORARY TABLE
用户主动创建的临时表,如果显式指定引擎为磁盘型,就绕过了内存路径:
-
CREATE TEMPORARY TABLE tmp_xxx ENGINE=InnoDB ...→ 直接磁盘 -
CREATE TEMPORARY TABLE tmp_xxx ENGINE=MyISAM ...→ 直接磁盘(旧版本) - 即使没写 ENGINE,但表中含
TEXT/BLOB,MySQL 也会自动选 InnoDB
注意:CREATE TEMPORARY TABLE 本身不触发内部临时表,它属于外部临时表;但它的存在常被误认为是“优化手段”,实际若滥用反而增加管理开销和锁竞争。
如何确认某次查询真用了磁盘临时表
EXPLAIN 看不到落盘动作,它只预估执行计划,不模拟内存压力。真正判断得靠运行时指标:
- 查状态变量:
SHOW STATUS LIKE 'Created_tmp%',重点对比Created_tmp_disk_tables是否增长 - 用
SHOW PROFILE FOR QUERY N,找Copying to tmp table on disk这一行耗时 - 检查
@@tmpdir下是否有未清理的#sql_*文件,说明磁盘临时表残留,可能已撑满磁盘 - 慢日志里配合
log_queries_not_using_indexes,定位具体哪条 SQL 在高频触发
最隐蔽的坑是:你看到 Using temporary 时,优化器已经放弃流式处理;此时调参只是延缓问题爆发,不是修复逻辑缺陷。


















