MySQL 8.0 复杂查询在 tmpdir 配置失效时,会优先走 TEMPTABLE 内存池(受 temptable_max_ram 控制),超限时直接写入 ibtmp1,而非 tmpdir;同时排序缓冲、LOAD DATA 等仍依赖 tmpdir,权限失败则静默报错。

MySQL 8.0 的临时目录配置不当,会让复杂查询在“看不见的地方”失败——不是报错退出,而是 silently 落盘、膨胀 ibtmp1、拖慢响应甚至填满磁盘。
tmpdir 配置失效后,复杂查询会走哪条路?
很多人以为改了 tmpdir 就万事大吉,其实 MySQL 8.0 有两套临时文件机制:一套走 tmpdir(MyISAM 临时表、排序缓冲、LOAD DATA 中间文件),另一套由 innodb_temp_tablespaces_dir 控制(InnoDB 内部临时表,默认是 ./#innodb_temp/)。但最关键的第三条路,是 TEMPTABLE 引擎的内存池——它根本不看 tmpdir,只认 temptable_max_ram。
当 tmpdir 不可写(比如权限被 SELinux 拦、PrivateTmp=yes 隔离),而你又没调高 temptable_max_ram,复杂查询(如带 GROUP BY + JSON_EXTRACT 或多层窗口函数)就会:
- 先尝试用 TEMPTABLE 在内存里算;
- 内存不够就直接写入 ibtmp1(InnoDB 临时表空间);
- 日志里看不到 /tmp 相关报错,SHOW VARIABLES LIKE 'tmpdir' 显示正常,但磁盘狂涨、查询变慢、甚至报 The table '/tmp/#sql' is full(路径名是假象,实际是 ibtmp1 满了)。
为什么改了 tmpdir 还是查不到临时文件?
因为很多临时操作根本不会落到你配的 tmpdir:
-
SELECT ... ORDER BY大结果集排序 → 若超出sort_buffer_size,优先走temptable_max_ram,超限则进ibtmp1,不碰tmpdir -
CREATE TEMPORARY TABLE ... SELECT→ MyISAM 类型才用tmpdir,InnoDB 类型走ibtmp1 -
ALTER TABLE ... ALGORITHM=INPLACE中间步骤 → 绝大部分用ibtmp1,和tmpdir无关 -
EXPLAIN FORMAT=TREE显示Using temporary→ 实际落盘位置取决于引擎和内存配额,不是tmpdir
验证方式:执行一个确定触发磁盘临时表的语句(如 SELECT * FROM information_schema.columns ORDER BY table_schema, column_name LIMIT 50000),然后:
- 查 ls -l /var/lib/mysql/tmp/(你配的 tmpdir)→ 很可能空空如也;
- 查 ls -lh /var/lib/mysql/ibtmp1 → 大小明显增长。
temptable_max_ram 和 tmpdir 必须协同调整
tmpdir 是“老派路径”,temptable_max_ram 才是 MySQL 8.0.16+ 复杂查询的真正闸门。两者不匹配,就会出现“配置写了却没效果”的幻觉:
- 只改
tmpdir但temptable_max_ram还是默认 3% 总内存 → 大查询照样爆ibtmp1 - 只调高
temptable_max_ram但tmpdir权限不对 → 排序缓冲、LOAD DATA仍失败,报Can't create/write to file '/tmp/ibXXXXX' - 两个都调了,但没确认
mysql用户对新tmpdir有真实写权限 →sudo -u mysql touch /path/to/tmp/test必须成功,否则启动时静默失败
temptable_max_ram 不支持运行时修改,必须写进 my.cnf 的 [mysqld] 段;单位推荐用 4G(不是 4096M),且别超过物理内存 15%,否则可能触发系统 OOM。
最容易被忽略的验证点:服务到底读了哪个配置?
Windows 下 MySQL 8.0 只认固定路径的 my.ini:C:\ProgramData\MySQL\MySQL Server 8.0\my.ini(隐藏目录),不是你放哪它就加载哪。Linux 下也一样,mysqld --verbose --help | grep "Default options" 输出的才是真实搜索路径。更隐蔽的是服务注册参数:sc qc mysql80 查 BINPATH,如果含 --defaults-file=,那你的 my.cnf 再完美也没用。
最终生效值永远以 SELECT @@tmpdir; 和 SELECT @@temptable_max_ram; 为准——配置文件写了≠MySQL 加载了≠参数真生效。


















