前几天项目同事突然反馈,MySQL的/data盘,500G的空间眼看着就要被撑爆了,仅剩30G。登录服务器一看,好家伙,ls -lh mysql-bin.* 列出来,瞬间心跳加速:
1.1G mysql-bin.000239
1.1G mysql-bin.000240
1.1G mysql-bin.000241
...
1.1G mysql-bin.000338
足足一百多个文件,每个1.1G,加起来接近110G,直接吃掉了磁盘20%的容量。很多人第一反应可能就是直接 rm -rf mysql-bin.*。打住,千万打住!这一步要是手滑,主从复制瞬间崩溃,业务延迟甚至数据不一致,后果不堪设想。
一、先搞清楚:binlog 究竟是做什么用的?
不少开发者提到binlog,第一反应就是“主从同步”。那具体流程是怎样的?其实过程非常清晰:
主库写入 binlog → 从库IO线程拉取 → 写入 relay log → SQL线程重放 → 数据同步完成
这里面有一个关键点:从库已经读取并应用完成的binlog,主库才能安全删除。否则,你会看到从库报出这样的错误:
Could not find first log file name in binary log index file
replication stopped
复制直接中断,这就是随意删除的代价。
二、正确的排查流程(生产级标准步骤)
别急着动手删,先确认几件事。第一步,检查主从同步状态是否正常。
1. 查看主库状态
MySQL 8之前使用 SHOW MASTER STATUS;,MySQL 8+则用 SHOW BINARY LOG STATUS;。
2. 查看从库状态
老版本用 SHOW SLAVE STATUS\G,MySQL 8.0.22之后,推荐用 SHOW REPLICA STATUS。输出结果中,有几个字段是重点关注对象:
Source_Log_File
Relay_Source_Log_File
举个例子,如果输出显示 Source_Log_File: mysql-bin.000057,这就意味着从库已经同步到了第57号文件。结论很清楚:57号之前的binlog文件,都可以安全清理掉。
三、安全清理 binlog 的正确操作
1. 错误操作(千万别碰)
直接在操作系统层面 rm -rf mysql-bin.*,后果已经说过,复制必然中断。
2. 正确但不推荐的方式
使用 PURGE BINARY LOGS; 全部清空,这一操作太暴力,生产环境要慎用。
3. 推荐的最佳实践
使用这条命令:PURGE BINARY LOGS TO 'mysql-bin.000057';,含义很清楚:删除57号之前的所有文件。安全、可控,而且立竿见影。
执行完再跑一下 df -h,看着磁盘空间唰地就释放出几十个G,那感觉确实很爽。
四、根治方案:自动过期清理(强烈建议)
很多环境binlog堆积成山的根本原因,就是没有设置过期策略。先看看当前的配置:
binlog 过期时间
用 SHOW VARIABLES LIKE 'binlog_expire_logs_seconds'; 查看,比如看到 2592000,换算一下:2592000 ÷ 86400 = 30天,也就是保留30天。
单个文件大小
SHOW VARIABLES LIKE 'max_binlog_size'; 默认是1G,所以看到1.1G的文件很正常。
安全写入参数
SHOW VARIABLES LIKE 'sync_binlog';,推荐设置为1,这是生产环境的标配,能有效避免宕机时丢失binlog。
五、生产环境最佳实践
如果遇到空间告急,可以临时调整一下过期时间:
mysql> SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
+----------------------------+---------+
| Variable_name | Value |
+----------------------------+---------+
| binlog_expire_logs_seconds | 2592000 |
+----------------------------+---------+
1 row in set (0.06 sec)
mysql> SET GLOBAL binlog_expire_logs_seconds = 604800;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
+----------------------------+--------+
| Variable_name | Value |
+----------------------------+--------+
| binlog_expire_logs_seconds | 604800 |
+----------------------------+--------+
1 row in set (0.01 sec)
但要注意,这种方式重启MySQL后会失效。所以,一定要在配置文件里加上持久化设置:
[mysqld]
log-bin=mysql-bin
binlog_expire_logs_seconds=604800 # 7天
max_binlog_size=512M
sync_binlog=1
推荐策略就是:控制文件大小、设置合理的保留天数、确保写入安全。做到这几点,binlog空间问题基本就能彻底解决了。
