调大tmp_table_size无效是因为MySQL取tmp_table_size与max_heap_table_size的较小值作为内存临时表上限;若后者仍为默认16MB,则实际阈值仍是16MB,导致查询持续落盘。

为什么调大 tmp_table_size 却没效果?
因为 MySQL 内部临时表的内存上限取 tmp_table_size 和 max_heap_table_size 的较小值。哪怕你把 tmp_table_size 设成 256M,但 max_heap_table_size 还是默认的 16M,那实际能用的还是 16M。
常见错误现象:SHOW VARIABLES LIKE 'tmp_table_size' 显示已生效,但 SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables' 持续上涨,说明临时表仍在落盘。
- 必须同时设置两个参数,且数值完全一致(例如都设为
268435456) - 使用
SET GLOBAL修改后,仅对新连接生效;已有连接仍沿用旧值 - 线上环境建议在低峰期执行,并确认用户具备
SYSTEM_VARIABLES_ADMIN权限
哪些 SQL 会强制触发内存临时表?
不是所有带 GROUP BY 或 ORDER BY 的语句都会用临时表——关键看是否能走索引覆盖。真正触发 Using temporary 的典型场景包括:
-
SELECT DISTINCT a, b FROM t ORDER BY c(ORDER BY字段不在SELECT列中,且无索引) -
SELECT a FROM t1 JOIN t2 ON ... GROUP BY t1.x, t2.y(跨表分组,无法用单表索引优化) -
UNION(去重逻辑强制建临时表;改用UNION ALL可跳过)
查执行计划时,只要 EXPLAIN 输出里出现 Using temporary,就要立刻检查是否真落到磁盘——别只盯着“用了临时表”,要看 Created_tmp_disk_tables / Created_tmp_tables 比值是否超过 20%。
显式创建 TEMPORARY TABLE 时怎么选引擎?
手动建临时表时,别默认用 InnoDB。对中间结果做快速聚合、排序或分页,优先指定 ENGINE=MEMORY:
CREATE TEMPORARY TABLE daily_agg ( user_id INT, total DECIMAL(10,2) ) ENGINE=MEMORY;
注意三点:
-
MEMORY表不支持TEXT/BLOB字段,否则建表失败 - 如果后续要加索引,
HASH索引只支持等值查询(=),范围查询(>,BETWEEN)必须用BTREE - 每行数据固定长度,
VARCHAR(255)实际按 255 字节分配,容易浪费内存——能用CHAR就别用VARCHAR
内存不是越大越好,高并发下容易翻车
tmp_table_size 和 max_heap_table_size 是**每个连接**都可能独占的内存上限。设成 512M 看似爽,但 100 个并发连接就可能吃掉 50G 内存,触发 OOM 或 swap。
安全边界参考:
- 不超过物理内存的 10%~15%
- 必须小于
innodb_buffer_pool_size的 50%,避免挤占缓冲池 - 若业务有突发大查询,建议配合
max_connections做压测验证
真正难的不是调参,是判断哪条 SQL 值得给内存——先用 EXPLAIN 定位 Using temporary,再看 Created_tmp_disk_tables 是否持续增长,最后才动参数。盲目调大,不如删掉一个没用的 DISTINCT 或补一个联合索引。


















