MySQL 的 binlog 把磁盘写满导致实例起不来
问题现象
一台主库某天夜里突然连不上,业务大面积报错。登上去看,MySQL 进程在反复重启,错误日志里反复出现一行:
[ERROR] Could not open file /var/lib/mysql/binlog.000312 for reading: No space left on device
df -h 一打,根分区 100% 满。但 du 扫下来业务表数据也就几十 G,剩下的空间全被一堆 binlog.000xxx 文件吃掉了,单个就有上 G,几百个连在一起直接把盘塞死。更糟的是实例此时已经起不来,因为启动时要读取 binlog 做恢复,而盘满了根本打不开文件。
排查过程
先确认空间确实被 binlog 占着:
ls -lh /var/lib/mysql/binlog.* | wc -l
du -sh /var/lib/mysql/binlog.*
输出几百个文件、合计七八十 G,方向就明确了。接着看 binlog 的保留策略:
mysql -e "SHOW VARIABLES LIKE 'expire_logs_days';"
mysql -e "SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';"
结果发现两个都是 0 / OFF——也就是说从没设过自动清理。再看是不是主从复制拖了后腿:
mysql -e "SHOW SLAVE STATUS\G"
SHOW BINARY LOGS;
这台是主库,从库一直正常在消费,所以不是复制位点卡住的问题,纯粹是没人清理。
根因
binlog 是 MySQL 的二进制日志,默认会一直写、一直累积,本身不会自动删除。是否清理取决于两个参数:expire_logs_days(按天)或 MySQL 8.0 后的 binlog_expire_logs_seconds(按秒)。两个都没配,binlog 就会无限增长。正常它会"在日志轮转或启动时"按过期策略删除旧文件,但前提是得有触发清理的时机且盘没满。盘一旦写满,实例连启动恢复都做不了,形成死循环:要清理得先启动,要启动盘得有空间,盘满又起不来。
解决办法
第一步先腾出空间让实例能起来。手动删掉最老的、确定不再需要的 binlog:
# 先看当前在用哪个,别误删
mysql -e "SHOW MASTER STATUS;"
# 只保留最近几个,其余 purge
mysql -e "PURGE BINARY LOGS TO 'binlog.000300';"
# 或者按时间
mysql -e "PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;"
如果实例压根起不来,先手动 rm 掉最老的一批文件腾出空间,再启动。起来后立刻设过期策略,二选一:
# my.cnf
expire_logs_days = 7
# 或 MySQL 8.0
binlog_expire_logs_seconds = 604800
设完 systemctl restart mysql 或在线 SET GLOBAL binlog_expire_logs_seconds=604800;。另外加一条监控:binlog 目录占用超过阈值就告警,别再等盘满了才发现。
小结
binlog 不会自己消失,过期策略一定要显式配。我后来养成习惯,新实例上线第一件事就是确认 binlog_expire_logs_seconds 不是默认值 0。盘满导致起不来时别慌,先 PURGE 或手动删老文件腾空间再起,根上还是把自动清理和监控补上,别让它在半夜咬你一口。
- 点赞
- 收藏
- 关注作者
评论(0)