MySQL binlog清理必须用PURGE BINLOGS命令,不可直接rm;expire_logs_days需配合FLUSH LOGS生效,8.0.11+改用binlog_expire_logs_seconds;删前须确认从库位置、文件存在性及磁盘句柄占用。

binlog 清理前必须确认 expire_logs_days 是否生效
MySQL 的 expire_logs_days 不是“定时自动删”,而是依赖 FLUSH LOGS 或每日启动时的清理逻辑 —— 很多实例压根没触发过,日志就一直堆积。查当前设置:
SHOW VARIABLES LIKE 'expire_logs_days';如果返回 0,说明完全不自动清理;如果是非 0 值(比如 7),还得看是否真有定期
FLUSH LOGS 或 MySQL 重启行为。
实操建议:
- 别只改配置就以为万事大吉,
SET GLOBAL expire_logs_days = 7;后必须手动执行一次FLUSH LOGS;才会触发首次清理 - 生产环境建议配合 crontab 每日执行
mysql -e "FLUSH LOGS;",否则过期逻辑永远“睡着” - 注意:该变量在 MySQL 8.0.11+ 已被弃用,改用
binlog_expire_logs_seconds(单位秒),设为 604800 等效于 7 天
安全删除 binlog 的唯一可靠命令是 PURGE BINLOGS
直接 rm -f 删除 mysql-bin.* 文件会导致 MySQL 崩溃或主从断裂 —— 因为文件名、索引偏移、GTID 位置都硬编码在 mysql-bin.index 和内存中。必须走 MySQL 自己的清理路径。
实操建议:
- 按时间删:
PURGE BINLOGS BEFORE '2024-05-01 00:00:00';(注意格式严格为Y-m-d H:i:s) - 按文件名删(更精准):
PURGE BINLOGS TO 'mysql-bin.000123';—— 会删掉所有 早于 该文件的日志(不含它自己) - 执行前先查现状:
SHOW BINLOG EVENTS IN 'mysql-bin.000122' LIMIT 1;确认目标文件存在且未被从库拉取 - 主从架构下,务必先检查从库 IO 线程位置:
SHOW SLAVE STATUS\G中的Relay_Master_Log_File,确保不删掉从库还没读的文件
误删 binlog 后无法回滚,但可快速止损
PURGE BINLOGS 是立即生效的 DDL 操作,没有事务、不能 ROLLBACK。一旦删错,恢复只能靠备份 —— 这也是为什么很多人不敢动。
实操建议:
- 删之前导出当前列表:
mysql -e "SHOW BINARY LOGS;" > binlog_list_$(date +%s).txt - 对关键业务库,建议用
--read-only=1启动临时只读实例,把待删 binlog 全部mysqlbinlog解析出来存档(虽然慢,但保底) - 如果已删错且无备份,立刻停写、停止
FLUSH LOGS,避免新日志覆盖磁盘残留数据 —— 有些情况下还能用extundelete或photorec挽救(仅限 ext4 且未被覆盖)
空间没释放?可能是 innodb_file_per_table=OFF 或日志文件被进程占用
执行 PURGE BINLOGS 后 df -h 看磁盘没变小,常见两个原因:一是 InnoDB 系统表空间(ibdata1)撑满且未启用独立表空间;二是 mysqld 进程仍持有已删文件句柄(Linux 下文件名删了但空间未释放)。
实操建议:
- 查句柄:
lsof | grep mysql-bin | grep deleted,若输出非空,说明 mysqld 还在读旧日志,需重启 MySQL(主从场景下先切流) - 确认存储引擎配置:
SHOW VARIABLES LIKE 'innodb_file_per_table';,若为OFF,InnoDB 数据和索引全挤在ibdata1里,删 binlog 对它毫无影响 - binlog 文件本身是纯顺序写,删完即释放,不涉及碎片整理 —— 不用跑
OPTIMIZE TABLE或其他维护命令
真正卡住人的从来不是命令记不牢,而是删之前没看从库位置、没核对 expire_logs_seconds 是否生效、或者以为 rm 跟 PURGE 是一回事。这些点漏一个,轻则空间白清,重则主从断开。


















