必须先释放磁盘真实空间,再操作:先df -h确认/var/lib/mysql所在分区使用率,再lsof +l1清理幽灵文件,purge binlog前核对主从位置,alter table engine=innodb或optimize table释放.ibd空间,tmpdir须避开/tmp内存盘,恢复顺序为查磁盘→清空间→开写→调参。

磁盘空间不足导致 MySQL 服务不可写或直接崩溃,不能靠 SET GLOBAL read_only = 0 或重启就解决——必须先释放出真实可用空间,否则所有写操作仍会失败,甚至无法完成启动。
先确认是不是真磁盘满了,而不是“幽灵文件”占着不放
很多人一看到 ERROR 1290 或 The MySQL server is running with the --read-only option 就急着关 read_only,结果命令执行成功,INSERT 还是报错。真正该盯的是磁盘本身:
- 运行
df -h,重点看/var/lib/mysql所在挂载点(比如/dev/vda1),不是根目录/ - 查 MySQL 实际数据目录:
mysql -e "SHOW VARIABLES LIKE 'datadir';",避免软链接误导 - 如果
df显示 100% 但du -sh /var/lib/mysql总和小得多,大概率是“幽灵文件”:进程删了文件但还在写,空间没释放 - 立刻执行
lsof +L1,过滤出状态为deleted的大文件,重点关注/var/lib/mysql/下的#sql_*、ibtmp1、mysql-bin.*
PURGE BINARY LOGS 前必须核对主从位置,否则主从立刻断
PURGE BINARY LOGS 是最常用也最容易踩坑的操作。它不会自动识别从库进度,直接删掉从库还没读完的日志,主从同步瞬间中断:
- 先查从库状态:
SHOW SLAVE STATUS\G,记下Relay_Master_Log_File和Exec_Master_Log_Pos - 再查当前 binlog 列表:
SHOW BINARY LOGS;,确保你要 PURGE 的文件名早于Relay_Master_Log_File - 安全命令只用时间锚点:
PURGE BINARY LOGS BEFORE '2026-08-10 00:00:00';(比当前时间早 1 天) - 长期预防:在
my.cnf中设expire_logs_days = 3,并验证生效:SELECT @@expire_logs_days;
大表 DELETE 后 .ibd 文件不缩容?别硬等自动回收
InnoDB 设计上就不会把已删行的空间还给文件系统。DELETE 掉 90% 数据,.ibd 文件大小纹丝不动——这不是 bug,是正常行为。不干预,空间永远卡在峰值:
- 确认
innodb_file_per_table = ON(默认开启),否则所有表共用ibdata1,删表也无效 - 线上表可执行
ALTER TABLE t1 ENGINE=InnoDB;,效果同OPTIMIZE TABLE,语义更清晰 - 若表太大不敢锁,且业务不能停写,别硬扛:用
pt-online-schema-change或导出+DROP+重建(务必提前停写或加锁) -
TRUNCATE TABLE比DELETE FROM快得多,且立即释放空间,但不可回滚、重置自增 ID
tmpdir 指向 /tmp?这才是压垮骆驼的最后一根稻草
MySQL 初始化或大查询时会在 tmpdir 写临时文件,而 /tmp 常为 tmpfs(内存挂载),大小仅 RAM/2 或固定 2–4GB。安装失败报 No space left on device,但 df -h / 显示还有 50GB,基本就是卡在这儿:
- 定位瓶颈:
df -h | grep -E '(tmp|var)'看真实使用率;ls -ld /tmp确认是否 tmpfs - 查真实路径:
SELECT @@tmpdir; - 绕过限制:安装前设环境变量
TMPDIR=/data/tmp;APT 安装用sudo apt -o DPkg::Install::TempDir=/data/tmp install mysql-server - 长期解法:把
tmpdir和innodb_tmpdir改到独立小 SSD 分区,或设为/dev/shm(需控制查询复杂度)
恢复顺序不能乱:查磁盘 → 清空间 → 再开写 → 最后调参。最常被忽略的是 lsof +L1 和 tmpdir 路径误判——这两处不查清,其他操作都是白忙。











