磁盘一夜之间满了,我查到是 MySQL 的 binlog 在悄悄长大
早上收到监控告警,说一台小服务器磁盘使用率超过 90%。登录上去 df -h 一看,根分区只剩不到 500M。再晚一点发现,MySQL 可能都要因为写不进盘而停掉。
这台机器跑了半年多,业务量也不大,我一直没操心过磁盘。结果就是典型的“没人管的东西迟早出事”。
先找到是谁占了地方
磁盘满了别急着删东西,先定位:
# 看哪个目录最大
du -sh /var/* 2>/dev/null | sort -rh | head -5
# 再往下钻一层
du -sh /var/lib/mysql/* 2>/dev/null | sort -rh | head -10
结果很清楚:/var/lib/mysql 占了将近 30G,里面一堆 binlog.000123 之类的文件,每个 1G,总共二十多个。业务表本身才 2G 不到,日志比数据大了十几倍。
binlog 是什么,为什么会失控
binlog 是 MySQL 的二进制日志,记录所有写操作,用来做主从复制和时间点恢复。它本身不是坏东西,问题是默认配置下它不会自己清理。
我查了当时的配置:
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
-- 结果是 2592000,也就是 30 天
30 天的保留期听起来合理,但这台机器每天的写操作不少,30 天攒下来就是 30G。更关键的是,这台机器是单机,既没有从库,也从来没做过时间点恢复——binlog 对它来说几乎没用,却一直在默默吃磁盘。
先救急:手动清理过期的 binlog
磁盘只剩几百兆,先腾地方再说。千万别直接 rm binlog 文件——MySQL 还在通过索引文件管理它们,直接删文件会让索引和实际文件对不上号,轻则报错,重则起不来。
正确做法是在 MySQL 里 purge:
-- 删除 7 天以前的 binlog
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);
-- 或者删到某个文件之前
PURGE BINARY LOGS TO 'binlog.000130';
我当时选了保留 7 天,一条命令下去腾出了 20 多 G。磁盘告警解除,服务也没受影响——不过执行前我还是先确认了没有从库在依赖这些日志,单机环境才敢这么干。有从库的话,purge 之前必须确认从库已经同步到了要删的位置。
根治:让它以后自己清理
救急之后要把保留策略改掉,不然 30 天后同样的问题再来一次:
-- 动态改掉,立即生效(MySQL 8.0)
SET PERSIST binlog_expire_logs_seconds = 604800;
604800 秒就是 7 天。这个值按自己机器的磁盘和业务量来定:磁盘小的、写入量大的,3 天也行;有从库或者要做时间点恢复的,留够从库追赶和恢复窗口再说。
我还在配置文件里也写了一份,免得下次重启又回到默认值:
# /etc/mysql/my.cnf 或 mysqld.cnf 的 [mysqld] 段
binlog_expire_logs_seconds = 604800
顺手又加了个磁盘使用率告警的阈值,从 90% 降到 80%,留出反应时间。
顺便查的其他几个“磁盘刺客”
那天顺手把整台机器的磁盘大户都扫了一遍,发现 binlog 不是唯一的隐患:
# 没轮转的日志
du -sh /var/log/* | sort -rh | head -5
# 检查 logrotate 配了没
ls /etc/logrotate.d/
有个应用日志没配 logrotate,单个文件已经 4G。一起处理了:加了 logrotate 配置,maxsize 100M 加 rotate 5,超过 100M 就轮转,只留 5 份。
这件事留给我的教训
单机小服务器最容易犯的错,就是觉得“量小不用管”。binlog、慢查询日志、应用日志,这些东西都是静悄悄地长,半年不看就给你一个惊喜。现在我的习惯是:
- 新装 MySQL 第一件事就把
binlog_expire_logs_seconds按磁盘大小定好; - 不需要主从复制和时间点恢复的单机,可以考虑直接关掉 binlog(
disable_log_bin),少一个操心点; - 磁盘告警阈值设在 80%,别等 90% 才动手,那时候留给你的操作空间已经很小了。
磁盘满不是技术难题,是管理习惯问题。花十分钟把这些默认值改掉,比半夜起来救火强得多。
- 点赞
- 收藏
- 关注作者
评论(0)