直接结论:不是SQL写错了,是MySQL在/tmp里写排序文件时磁盘写满;需四步全做——查@@tmpdir、看df -h、换tmpdir路径、同步调大tmp_table_size与max_heap_table_size,漏一步即失效。

直接结论:不是SQL写错了,是MySQL在/tmp里写排序文件时磁盘写满——查@@tmpdir、看df -h、换路径、同步调大tmp_table_size,四步必须全做,漏一步就白忙。
怎么快速确认是不是tmpdir真满了?
别翻配置文件,运行时值才作数:
- 进MySQL执行:
SELECT @@tmpdir;——返回/tmp或空(表示用系统默认)都算危险信号 - 终端执行:
df -h $(mysql -Nse "SELECT @@tmpdir;")——重点看Size列:如果是tmpfs类型(比如/tmp常挂为内存盘),大小通常只有1–4GB,远小于物理磁盘,极易撑爆 - 顺手加一句:
df -i $(mysql -Nse "SELECT @@tmpdir;")——小临时文件极多时,可能空间剩得多但inode耗尽,No space left on device错误照样报
不重启MySQL怎么临时切走tmpdir?
5.7.20+支持SET GLOBAL动态切换,但三个条件缺一不可:
- 目标路径已存在,且MySQL进程用户(如
mysql)有读写权限(Linux下chown mysql:mysql /data/mysql-tmp,Windows下给SYSTEM“完全控制”) - 该路径所在分区剩余空间 ≥ 当前最大表大小 × 1.2(留缓冲余量,别只看当前查询)
- 执行后立刻验证:
SELECT @@tmpdir;必须返回新路径——否则配置没生效,旧进程还在用/tmp
示例:SET GLOBAL tmpdir = '/data/mysql-tmp';
为什么换了路径还报错?漏了这两个关键参数
真正麻烦的从来不是“换目录”,而是换完没人动tmp_table_size和max_heap_table_size。MySQL默认常为16MB,一旦排序字段没索引、JOIN字段类型不一致、或含TEXT/BLOB,就会强制落盘——哪怕你换了1TB的路径,它还是每16MB就写一次磁盘临时文件。
- 必须同步调大:
SET GLOBAL tmp_table_size = 268435456;(256MB)、SET GLOBAL max_heap_table_size = 268435456; - 注意:这两个值要设成一样,否则以小的为准
- 调参只是兜底,根源在SQL——给
ORDER BY/GROUP BY字段加联合索引,让MySQL能直接利用索引排序,跳过临时表,比调参见效更快
Windows phpEnv环境下的特殊陷阱
phpEnv默认把tmpdir指向C:/Windows/Temp或C:/phpEnv/tmp,这里容易静默失败:
- 路径必须用正斜杠:
tmpdir = D:/mysql_tmp(单反斜杠会被MySQL解析为转义) - 右键文件夹 →「属性」→「安全」→给
SYSTEM用户加「完全控制」权限(仅“修改”不够) - 改的是MySQL实际加载的配置文件(通常是
MySQL\my.ini,不是etc\my.ini) - 必须彻底杀掉所有
mysqld.exe进程(任务管理器里找),再重启服务——残留进程仍用老路径
换完路径不验证SELECT @@tmpdir;,等于没换;调了tmpdir不调tmp_table_size,等于白调。


















