MySQL 报 "Writing file" 错误(Errcode: 28)怎么办?
MySQL Errcode 28 = No space left on device,磁盘写满。用 perror 解码错误码,df -h/df -i 定位分区与 inode,清理 binlog/慢日志/临时表空间的完整处置流程与 FAQ。
Errcode 28 的含义是 No space left on device——MySQL 所在分区磁盘满了(或 inode 耗尽),写数据/临时文件/日志失败。 可用 perror 28 直接查看受支持的操作系统及部分 MySQL 错误码的文字解释。
perror 28
# 输出:OS error code 28: No space left on device
第一步:确认是哪个分区满了
df -h # 看各分区使用率,重点找 100% 的
df -i # 磁盘有空间也可能 inode 耗尽(海量小文件)
MySQL 常写满的位置:数据目录(默认 /var/lib/mysql)、tmpdir(排序/临时表)、binlog 目录、慢查询/错误日志。
第二步:找出空间杀手
du -sh /var/lib/mysql/* | sort -rh | head -20 # 哪个库/表最大
ls -lh /var/lib/mysql/ | grep -E 'binlog|\.log' | head
最常见的四个元凶:
- binlog 无限增长:主从或开了 binlog 没设过期——
SHOW BINARY LOGS;看数量,PURGE BINARY LOGS BEFORE '2025-01-01';清理,配置文件设binlog_expire_logs_seconds。 - 慢查询/general 日志:尤其 general log 忘关,几天能吃几十 G。
- 大临时文件:
tmpdir下#sql*.ibd之类,通常是巨型 ALTER 或排序撑的。 - Undo/表空间膨胀:长事务导致 undo 堆积,或 ibdata1 只增不减。
第三步:应急与根治
应急(先让 MySQL 能写):
-- 确认所有副本(含离线副本)和备份/PITR不再需要较旧binlog后执行
PURGE BINARY LOGS TO 'mysql-bin.000123';
-- 仅确认日志输出到TABLE且可丢弃该日志时:关闭并清空
SET GLOBAL general_log='OFF'; TRUNCATE mysql.general_log;
PURGE ... TO 保留指定的 mysql-bin.000123 本身,只删除更早的文件;不要手动删除 binlog、redo、undo 或表空间文件。
根治:
- binlog 设过期:my.cnf 里
binlog_expire_logs_seconds = 604800(7 天)。 - 日志接 logrotate;慢日志单独分区。
- 按需归档历史数据;OPTIMIZE TABLE 可能重建表并需要额外空间,不应在满盘时作为例行急救。
- 给磁盘设监控——下次别再让数据库先发现。
清理后,把 MySQL 数据目录与临时目录所在挂载点分别纳入监控。观测云 磁盘采集器可采集空间和 inode 用量;结合增长速度设置阈值,先核对挂载点标签,避免只监控系统盘而漏掉实际写入的分区。
常见问题(FAQ)
Q:df 显示还有空间,为什么还报 28?
A:两种可能:df -i 看 inode 是否耗尽(小文件太多);或者 MySQL 的 tmpdir 指向了另一个已满的分区(常见于 tmpdir=/tmp 而 /tmp 是小分区)。
Q:磁盘满了连清理命令都执行不了怎么办?
A:优先扩容或在保留策略允许下清理已确认可丢弃、非数据库内部的旧文件;不要任意 truncate 数据库或活动日志。先确认文件用途、备份和打开状态。
Q:清完空间后 MySQL 需要重启吗?
A:一般不用。但应核对错误日志、写入恢复和复制状态;是否需要表检查/恢复应按存储引擎和具体错误判断,不把满盘等同于表已损坏。