tmp_table_size和max_heap_table_size必须设为相同值,否则内存临时表上限取二者较小值;线上需同步配置并验证一致性,且需结合SQL优化而非盲目调大参数。

tmp_table_size 和 max_heap_table_size 必须设成一样
MySQL 内存临时表的实际大小上限,取的是 tmp_table_size 和 max_heap_table_size 二者中的较小值。哪怕你把 tmp_table_size 改成 512M,但 max_heap_table_size 还是默认的 16M,那所有连接里的临时表最多只能用 16M 内存——查询照样落到磁盘,Created_tmp_disk_tables 持续上涨。
线上配置必须显式同步设置,例如:
SET GLOBAL tmp_table_size = 268435456; SET GLOBAL max_heap_table_size = 268435456;
或者写进 /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',确认两者数值一致 -
SET GLOBAL只对新连接生效,旧连接仍用老值,别忘了重连验证 - 重启 MySQL 后,务必再查一遍变量,避免配置文件语法错误导致加载失败
为什么调了还是走磁盘临时表
参数设对了,Created_tmp_disk_tables 却没降,大概率不是参数问题,而是 SQL 本身触发了强制落盘逻辑。
- 字段含
TEXT、BLOB或定义过宽的VARCHAR(2000)→ MySQL 按最大可能长度预估内存,直接跳过内存表 - 用了
UNION、DISTINCT、GROUP BY配合非索引列 → 中间结果无法压缩,容易超限 - 执行计划里出现
Using temporary但没配合适当索引 → 即使内存够,也因结构不支持而退化 - 客户端连接已建立,
SET GLOBAL不影响它;得用SET SESSION tmp_table_size = ...单独调,或重连
tmp_table_size 不宜超过物理内存的 10%~15%
这个值是按每个连接独立分配的。一台 64G 内存的机器,如果设成 1G 并发 100 个连接,理论峰值就吃掉 100G 内存——系统开始 swap,InnoDB buffer pool 被挤占,整体性能雪崩。
- 建议从
64M或128M起步,结合Threads_connected和Created_tmp_disk_tables增速逐步上调 - 绝对不能超过
innodb_buffer_pool_size的 25%~30%,否则缓冲池抖动比临时表落盘还伤性能 - 混跑 OLTP + 报表的实例,优先按报表类 SQL 的中间结果大小来定,宁可略高,但必须留足余量
监控比调参更能定位真实瓶颈
盲目调大 tmp_table_size 是最常见误区。真正该盯的是状态变量和执行计划。
- 先运行
SHOW GLOBAL STATUS LIKE 'Created_tmp%',重点看Created_tmp_disk_tables / Created_tmp_tables比值:高于 10% 才值得优化 - 对高频慢查询跑
EXPLAIN FORMAT=JSON,检查used_temp_table是否为true,以及using_filesort是否同时出现 - 加
log_queries_not_using_indexes = ON和long_query_time = 0,抓出真正落地磁盘的语句,逐条分析索引覆盖和字段类型
参数只是兜底手段,SQL 写法才是根因。比如给 GROUP BY a, b 加联合索引,往往比把 tmp_table_size 从 128M 拉到 512M 更有效,也更安全。

















