tmp_table_size 和 max_heap_table_size 必须设为相同值,因为mysql内存临时表大小取二者最小值,若不一致会导致预期外的磁盘临时表;建议在my.cnf中显式设为相等(如256m),并结合监控created_tmp_disk_tables突增来动态优化。

tmp_table_size 和 max_heap_table_size 为什么必须设成一样
MySQL 用内存临时表时,实际受两个参数共同限制:tmp_table_size 和 max_heap_table_size。只要其中任意一个更小,就会触发磁盘临时表——不是看哪个大,而是取两者最小值。很多线上事故就卡在这儿:调了 tmp_table_size 却忘了同步改 max_heap_table_size,结果查询照常写 /tmp。
实操建议:
- 在
my.cnf中显式将两者设为相等,比如都设为256M,避免隐式截断 - 重启前用
SHOW VARIABLES LIKE 'tmp_table_size';和SHOW VARIABLES LIKE 'max_heap_table_size';确认生效 - 如果实例混跑 OLTP + 报表类查询,建议按峰值报表 SQL 的中间结果大小来定,宁高勿低(但别超物理内存 20%)
怎么确认是不是临时表真把磁盘打满了
别急着调参,先验证问题根源。磁盘满不一定是临时表导致的,也可能是慢查询日志、binlog、undo log 或其他进程占用了 /tmp 目录空间。
实操建议:
- 查 MySQL 实际用的临时目录:
SELECT @@tmpdir;,然后用df -h看对应挂载点使用率 - 查当前活跃的磁盘临时表数量:
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';,对比Created_tmp_tables,比值超过 10% 就值得警惕 - 用
lsof +D /tmp(或对应@@tmpdir路径)看哪些进程在写大文件,排除非 MySQL 进程干扰
ORDER BY、GROUP BY、DISTINCT 触发磁盘临时表的典型场景
这三个操作最容易悄悄生成大临时表,尤其当字段没索引、类型不匹配或排序字段过大时。比如 GROUP BY JSON_EXTRACT(col, '$.name') 会强制走磁盘临时表,因为函数结果无法用索引加速。
实操建议:
- 对
GROUP BY或ORDER BY字段加联合索引,覆盖所有参与列(包括 SELECT 列,避免回表) - 避免在
ORDER BY中使用函数或表达式;如必须用,考虑提前计算并存为生成列(Generated Column),再建索引 -
DISTINCT多字段时,检查是否真的需要全字段去重,有时加LIMIT或改用EXISTS子查询更省资源
tmpdir 挂到 SSD 或独立分区的实操注意点
就算调大内存参数,遇到超大中间结果还是得落盘。这时候把 tmpdir 指向高速存储能显著缓解 IO 压力,但有几个硬性约束容易被忽略。
实操建议:
- 确保目标路径有足够空间且 MySQL 进程有读写权限(
chown mysql:mysql /mnt/ssd/tmp) - Linux 下若挂载了
noexec或nosuid选项,MySQL 启动会失败,需在/etc/fstab中去掉 - 不要把
tmpdir设为/dev/shm——虽然快,但它是 tmpfs,大小受shmmax限制,且 MySQL 5.7+ 对它支持不稳定,容易报Can't create/write to file
临时表溢出本质是内存与磁盘之间的权衡点没找对。最麻烦的不是参数调多少,而是不同查询对临时表的需求差异极大——一条 GROUP BY 可能吃掉 2G 内存,另一条却只用 2MB。所以监控 Created_tmp_disk_tables 的突增比死磕固定数值更有价值。











