当MySQL所在服务器的磁盘被写满,实例往往会在重启或正常运行中突然崩溃,错误日志里经常出现“No space left on device”或者“Binlog has bad magic number”之类的提示。此时数据库无法完成正常的崩溃恢复,因为binlog目录同样没有剩余空间供新的日志写入。面对这种状况,最稳妥的思路是先释放binlog占用的空间,再让实例正常起来,而不是盲目扩容或重装。

一、确认磁盘与binlog占用情况
在动手删除任何文件之前,需要先确认确实是binlog占用了大量空间。可以通过Linux的磁盘查看命令快速定位。通常MySQL的数据目录在/var/lib/mysql,binlog文件以“mysql-bin”或类似前缀命名,形如mysql-bin.000001。
使用下面的命令查看各目录的占用比例:
df -h du -sh /var/lib/mysql/*
如果看到/var/lib/mysql下的binlog文件总大小达到几十GB,而磁盘可用空间为0,就可以判定问题根源。注意不要直接删除正在被MySQL进程持有的文件,否则可能造成文件句柄泄漏或数据不一致。
二、安全清理binlog的两种方式
1. 实例可短暂启动时用SQL清理
如果磁盘只是临界满,MySQL还能以跳过部分检查的方式起来,可以优先使用MySQL自带的日志清理指令。这种方式最安全,因为它只会删除已经关闭且不在用的历史日志。
先查看当前正在使用的binlog文件名:
SHOW MASTER STATUS;
然后删除指定文件之前的所有日志,例如当前是mysql-bin.000100,就执行:
PURGE BINARY LOGS TO 'mysql-bin.000100';
该命令会保留mysql-bin.000100及之后的文件,之前的全删掉。相比手动rm,它不会破坏MySQL的内部索引。缺点是如果磁盘已经完全写满导致起不来,这条路就走不通。
2. 实例彻底无法启动时手动删文件
当MySQL根本无法启动,只能先停掉服务,手动从操作系统层面删除一部分最早的binlog。关键是绝不能删掉mysql-bin.index文件和当前最新的那几个日志。
进入binlog目录,按时间排序找出最旧的一批文件:
cd /var/lib/mysql ls -ltr mysql-bin.*
删除例如最旧的20个(请确认这些不是服务崩溃时正在写的):
rm -f mysql-bin.000001 mysql-bin.000002 mysql-bin.000003
删完后要同步修改mysql-bin.index,把被删的文件名从里面去掉,否则MySQL启动时会因找不到文件而报错。可以用vi或sed处理该文本文件。
三、调整参数避免再次写满
设置自动过期时间
根本解决办法是让binlog自动清理。MySQL提供expire_logs_days参数(8.0后改为binlog_expire_logs_seconds),控制日志保留天数。例如保留3天:
SET GLOBAL expire_logs_days = 3;
同时在配置文件my.cnf写入,防止重启失效:
[mysqld] expire_logs_days = 3
这样MySQL会在日志切换时自动删除超过期限的binlog。如果业务量极大,还可以考虑将binlog放到独立磁盘,并配合监控告警,在占用达到阈值时提前介入。
评估是否需要关闭binlog
对于纯测试环境或不需要主从复制的单机,可直接关闭binlog来彻底避免该问题。在配置中注释掉log-bin行并重启即可。但生产环境通常不建议,因为会失去时间点恢复能力。
四、启动前的最终检查
清理完毕后,先执行df -h确认有足够空间,再启动MySQL。若仍起不来,查看错误日志是否指向某个binlog校验失败,可尝试用mysqlbinlog工具解析定位损坏文件并单独移走。
启动成功后,立即用SHOW BINARY LOGS;确认列表与磁盘文件一致。整个处理过程的核心原则就是:不删索引文件、不删当前日志、优先用SQL命令清理。