适用场景

MySQL 主库所在分区持续增长,/var/lib/mysql 已接近 100%,业务开始出现写入失败、事务提交变慢,甚至因为磁盘写满而触发只读保护。检查目录后发现大量 mysql-bin.000xxx 文件,但不能直接删除:binlog 仍可能被复制从库、增量备份或审计流程使用。

本文适用于已开启二进制日志、存在主从复制或备份链路的 MySQL 5.78.0 环境。

现象与风险

常见现象包括:

  • 监控中 MySQL 数据盘使用率持续爬升,binlog 占用远高于数据表;
  • SHOW BINARY LOGS 显示某几个文件异常大,或切换频率明显升高;
  • 从库 Seconds_Behind_Source 上升,复制位点仍停留在很旧的 binlog;
  • 磁盘满后出现 The MySQL server is running with the --read-only optionNo space left on device 或事务提交失败。

最危险的“快速修复”是直接在文件系统中 rm mysql-bin.*。这样会让 MySQL 的 binlog 索引与文件不一致;更严重的是,从库或备份恢复需要的日志会永久丢失。

第一步:确认增长位置和保留边界

先确认是 binlog,而不是临时文件、relay log 或慢日志占满了空间:

df -h /var/lib/mysql
sudo du -sh /var/lib/mysql/* | sort -h | tail -20
mysql -e "SHOW BINARY LOGS;"
mysql -e "SHOW VARIABLES LIKE 'log_bin%';"
mysql -e "SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';"
mysql -e "SHOW VARIABLES LIKE 'expire_logs_days';"

SHOW BINARY LOGS 中的 File_size 是 MySQL 认知的日志大小,优先以它作为清理依据。MySQL 8.0 使用 binlog_expire_logs_seconds;5.7 常见的是 expire_logs_days。若两者均未配置,日志会一直累积。

再确认所有复制消费者是否已越过准备清理的日志。MySQL 8.0 可在主库执行:

SHOW REPLICAS;
SHOW PROCESSLIST;

在每台从库执行:

SHOW REPLICA STATUS\G
-- MySQL 5.7 使用:SHOW SLAVE STATUS\G

重点记录 Source_Log_File(5.7 为 Master_Log_File)和 Read_Source_Log_Pos。计划删除的日志必须早于所有仍在工作的从库读取位点。若使用 GTID,还应记录 Executed_Gtid_Set,不要仅凭 Seconds_Behind_Source=0 判断安全:离线从库可能根本没有上报。

第二步:定位 binlog 为什么突然变大

先从 MySQL 错误日志、应用发布记录和 binlog 文件大小的时间段做关联。大事务、批量更新、全表导入、长时间未提交的事务,都可能造成单个 binlog 异常膨胀。

查看正在执行和未提交的事务:

SELECT trx_id, trx_started, trx_state, trx_rows_modified,
       trx_mysql_thread_id, trx_query
FROM information_schema.innodb_trx
ORDER BY trx_started;

SHOW FULL PROCESSLIST;

trx_rows_modified 很大且 trx_started 很早的事务,通常是优先排查对象。对于已完成的历史日志,可在复制出的文件或低峰时段解析,避免直接在生产主库上做重扫描:

mysqlbinlog --base64-output=DECODE-ROWS -vv \
  /var/lib/mysql/mysql-bin.000872 | less

观察连续大量 Update_rowsDelete_rowsWrite_rows 事件,以及一个 BEGINCOMMIT 之间是否包含数百万行。若是批处理任务导致,应把一次性大事务改为按主键范围分批提交,例如每批 1,000~10,000 行,并在批次间短暂让出资源。

第三步:建立可回滚的安全清理方案

清理前先确认最近一次可用全量备份,以及增量备份链路最后消费的 binlog 文件。对需要保留的边界,建议先制定明确清单:

最旧仍在线从库需要:mysql-bin.000860
备份工具最后消费到:mysql-bin.000858
因此最早可删除边界:mysql-bin.000858 之前

使用 SQL 命令清理,而不是删除目录文件:

-- 删除严格早于指定文件的 binlog;000858 本身会保留
PURGE BINARY LOGS TO 'mysql-bin.000858';

-- 或按时间清理,适合已确认保留期的环境
PURGE BINARY LOGS BEFORE '2026-08-15 00:00:00';

PURGE BINARY LOGS TO 的边界容易被误解:目标文件不会被删除。执行前用 SHOW BINARY LOGS 再核对一次文件名,并将命令与执行结果记录到变更单中。磁盘紧急时,也应优先暂停可重试的批处理写入,腾出安全窗口后再执行 purge;不要绕开 MySQL 的元数据直接删文件。

第四步:修复复制与备份的根因

如果最旧从库长期落后,先修复复制能力而不是无限保留日志:检查网络、SQL 线程报错、从库磁盘 IO 和并行复制配置。对于已无法追上且允许重建的从库,重新从主库备份搭建往往比保留数周 binlog 更可靠。

在确认恢复点目标(RPO)后设置自动过期策略。以下示例保留 7 天:

# /etc/mysql/my.cnf 或对应配置文件
[mysqld]
binlog_expire_logs_seconds = 604800
max_binlog_size = 256M

修改配置后重启前先按变更流程验证。max_binlog_size 只控制正常切换阈值,不能拆开一个正在提交的大事务;真正抑制突增仍要控制业务批处理的事务大小。对于使用物理备份工具的环境,还要确认其 binlog 归档任务成功后才允许过期。

监控与预防措施

建议同时监控容量、增速和复制保留边界:

  • 数据盘使用率在 70%、80%、90% 分级告警,并根据近 6 小时增长速度预测耗尽时间;
  • 采集 binlog 文件总大小、文件数量、最新文件增长速率;
  • 采集每个从库的读取日志文件和延迟,离线从库单独告警;
  • 对批量任务记录单批影响行数、事务持续时间和产生的 binlog 字节数;
  • 每次备份演练验证“全量备份 + 保留 binlog”能恢复到目标时间点。

总结

binlog 吃满磁盘时,核心不是尽快删文件,而是先划清复制与备份的安全边界,再用 PURGE BINARY LOGS 让 MySQL 一致地回收空间。之后通过限制大事务、修复落后从库、配置合理保留期和容量预测,才能避免同类事故再次发生。