基本可断定是tmpdir分区空间不足;因online ddl需在tmpdir写入排序文件、中间表等,而默认/tmp常为tmpfs内存挂载,容量受限于ram,远小于物理磁盘。

修改大表结构时提示 The table '#sql-xxx' is full 或 OS error code 28: No space left on device,基本可以断定是 tmpdir 所在分区空间不足——不是数据目录不够,而是 MySQL 在 DDL 过程中用临时目录存排序文件、中间日志或重建表的缓冲区,而默认指向 /tmp,它常是内存挂载(tmpfs),大小受限于 RAM。
为什么改表会爆 tmpdir 空间?
Online DDL 中部分操作(如 ALTER TABLE ... MODIFY COLUMN、ADD INDEX、ENGINE=InnoDB)需生成临时排序文件或中间表。这些文件不走 datadir,而是写入 tmpdir(UNIX/Linux 下)或 innodb_tmpdir(仅 InnoDB 临时表)。默认值多为 /tmp,而该目录常被挂为 tmpfs,容量等于内存一半甚至更小。
常见触发场景:
- 对 >1GB 的表执行
MODIFY字段类型,排序阶段生成数 GB 临时文件 - 未显式设
innodb_tmpdir,但tmpdir指向/tmp,而/tmp只有 2GB -
innodb_online_alter_log_max_size不够,导致并发 DML 日志撑爆tmpdir
怎么确认当前 tmpdir 和实际空间?
别猜,直接查:
登录 MySQL 执行:
SELECT @@tmpdir, @@innodb_tmpdir;多数情况返回
/tmp 或空(表示用系统默认)。
再登系统终端,确认该路径真实可用空间:df -h /tmp(若返回 tmpfs 类型,看 “Size” 列是否远小于物理磁盘)df -i /tmp(检查 inode 是否耗尽,尤其当小临时文件极多时)
注意:innodb_tmpdir 优先级高于 tmpdir,但只影响 InnoDB 临时表;排序类操作仍走 tmpdir。
临时救急:不重启就切走 tmpdir
生产环境不能随便重启 MySQL,可动态重定向(5.7.20+ / 8.0.12+ 支持):
执行:
SET GLOBAL tmpdir = '/data/mysql-tmp';确保该路径已存在、MySQL 用户有读写权限、且所在分区剩余空间 ≥ 表大小 × 1.2(留余量)。
验证是否生效:SELECT @@tmpdir; 必须立刻返回新路径。
⚠️ 注意事项:
- 该设置在重启后丢失,需写入
my.cnf的[mysqld]段落固化:tmpdir = /data/mysql-tmp - 若
/data/mysql-tmp是新挂载点,务必chown mysql:mysql /data/mysql-tmp - 不要设成 NFS 或慢速存储,否则 DDL 性能暴跌
长期规避:从配置和操作习惯上堵死漏洞
光改 tmpdir 是治标。真正要防住,得组合动作:
调整关键参数(写进 my.cnf):
-
tmpdir = /data/mysql-tmp(明确指向大空间本地盘) -
innodb_tmpdir = /data/mysql-tmp(避免 InnoDB 临时表也挤/tmp) -
innodb_online_alter_log_max_size = 536870912(512MB,防止日志文件无节制增长) -
sort_buffer_size = 2M、join_buffer_size = 4M(别设成几百 MB,单查询就能吃光tmpdir)
DDL 操作前必做检查:
- 用
du -sh /var/lib/mysql/your_db/your_table.ibd确认原表大小 - 确保
tmpdir分区剩余空间 ≥ 原表大小 × 1.5(重建类操作需双倍空间) - 避开业务高峰执行,减少
innodb_online_alter_log_max_size被打爆风险
最常被忽略的一点:即使你把 tmpdir 改到大分区,如果没同步调低 sort_buffer_size,单个大排序仍可能生成海量小临时文件,迅速耗尽 inode——所以 df -i 和 df -h 必须一起看。











