MySQL binlog清理指南:PURGE原理与从库安全实践

发布时间:2026/10/21 15:30:02
MySQL binlog清理指南:PURGE原理与从库安全实践 做 MySQL 运维迟早会撞上这么一件事磁盘告警刷屏df -h 一看数据目录被 binlog 日志文件占了八九成。这时候你脑子里只有一个念头——把没用的 binlog 删了。但真到了操作台前很多人会犹豫直接 rm 删文件还是执行 PURGE 语句如果从库还没读到那个文件删了是不是就崩了这些问题不搞清楚删日志就会变成删数据。这篇文章就是专门讲 MySQL 的 binlog 日志文件到底该怎么删。我会从 binlog 的产生原理说起然后梳理手动清理、自动过期、彻底重置三种方式的适用场景接着聊聊文件层面删除和空间释放的真相最后用一次生产环境磁盘告警的复盘把我踩过的坑和总结出的检查清单一起给你。不管你是刚接手数据库的新手还是已经写过一堆维护脚本的熟手这篇文章都能让你在下一次清理 binlog 的时候心里更有底。1. 先搞清binlog为什么值得被科学地删一上来就动手删很容易翻车。先花几分钟搞懂 binlog 是什么才能知道哪些能删、哪些不能删。很多维护事故不是命令不会敲而是没搞清楚日志和复制、恢复之间的依赖关系。1.1 binlog到底在记什么为什么一天能堆几十个文件简单说binlog二进制日志是 MySQL 在 Server 层产生的一份追加写日志专门记录所有导致数据变动的操作。INSERT、UPDATE、DELETE、DDL甚至对表结构有影响的语句都会被记进去。为什么需要它主要两个用途第一主从复制。从库通过 io_thread 拉取主库的 binlog再通过 sql_thread 重放让主从数据保持一致第二基于时间点恢复PITR。当你把全量备份恢复到某个时间点后需要继续回放从那个时间点之后的 binlog把数据补到故障前的状态。你可以用下面这条语句看当前节点上一共有多少个 binlog 文件以及每个文件的大小执行后输出结果类似这样这里我截取了三行Log_nameFile_sizeEncryptedmysql-bin.0000011073741824Nomysql-bin.0000021073741824Nomysql-bin.000003734003200Nobinlog 文件默认单个大小上限是max_binlog_size参数控制默认值是 1GB。但实际生产中经常看到一堆几百MB的文件因为事务大、写入快binlog 文件轮转得就快。假设一个业务一天产生 10GB 的变更日志那就是 10 个文件起步如果 30 天不清理300 个文件、300GB 就这么堆出来了。等你想清理的时候光看文件列表就已经头晕了。1.2 日志越堆越多的三个隐形推手很多人以为 binlog 多一定是业务量大其实业务量只是最直白的原因。这里要特别警惕日志增长往往是几个因素叠加在一起被放大的结果并不是单一问题。至少有三个容易被忽略的推手大事务批量更新。一条 UPDATE 语句更新几百万行会一次性生成好几GB的 binlog文件数量瞬间涨上去。这种大事务不仅让 binlog 增长还会对主从延迟产生压力。没有开自动清理或清理窗口被配置成了 0。MySQL 默认是否清理取决于版本和参数。MySQL 5.7 及以前用expire_logs_days默认值为 0表示不过期MySQL 8.0 开始默认binlog_expire_logs_seconds2592000秒30 天。如果还沿用老版本的习惯或者配置文件中写的是 0就等于自动清理完全没生效。全量备份时间拉得过长。做全量备份时为了数据一致性binlog 不能清如果备份要跑 4 小时那么这 4 小时内新产生的 binlog 只能等下次清理窗口。备份越久清理空窗期越长。这还不算频繁的小批量写入——每次事务提交都会生成日志记录还没到 1GB 就轮转了于是文件数量又多又碎。线上真实案例往往是这三个因素同时出现日志增长速度远超你的预期。1.3 不清理时代价不只是磁盘告警磁盘被占满是最表面的代价再往下挖还有三个问题。第一MySQL 在写 binlog 时需要保证顺序写入的 IO 性能如果磁盘剩余空间太少或者日志文件碎片化事务提交耗时会明显增加线上表现为响应时间抖动。第二全量备份工具在扫描 binlog 或定位位点时会变慢恢复演练时如果日志文件特别多从备份点回放的时间也会被拉长RTO 变大。第三也是最容易被忽视的文件数量一多复制线程在切换文件时可能需要频繁打开新文件对从库来讲网络 IO 和本地读盘在高峰时会互相争抢资源延迟就是这么加出来的。所以我一直推荐的做法是binlog 不是不删而是要科学地删——保留一个能覆盖恢复窗口的周期其余全部交给自动清理或定时 PURGE。下面这些章节就是怎么科学地删。2. 删除前必须回答的三个能不能删binlog 删除是个高收益、高风险的动作动手前问自己三个问题答案不齐就宁可不删。这里说的风险不一定是删除动作本身而是删除后连带出来的复制中断、恢复窗口丢失那才是真正致命的。2.1 我的从库是否已经追完将要删除的日志主从复制是最脆弱的依赖关系。假设你有一主两从两个从库当前读到的位点分别是 mysql-bin.000005 和 mysql-bin.000008。这时候你打算把 mysql-bin.000008 之前的日志全部清掉那第二个从库肯定没问题但第一个从库还没读到 000005 以后的数据一旦 000005、000006 等文件被删它就会报错错误码通常是 1236从主服务器读取日志时找不到匹配的日志事件。这是因为主库的日志索引里已经不存在从库需要的日志文件从库无法继续拉取。所以清理前一定要先看每个从库的状态SHOW SLAVE STATUS\G重点看两个字段Master_Log_File是 io_thread 正在从主库拉取的文件名Relay_Master_Log_File是 sql_thread 正在重放的文件名。这两个字段代表从库此刻在用的 binlog 文件。你在主库执行 PURGE 的时候MySQL 会内部检查这些文件是否被从库引用如果从库还挂在旧文件上主库在有些版本下会等待或失败。所以最稳妥的办法不是依赖 MySQL 的检查而是先确认所有从库的Read_Master_Log_Pos都已经追过你想删除的最新文件之后再动手。一个简单的经验是保留文件的下限取所有从库中最落后的那个Master_Log_File然后在这之前再留一两个文件做缓冲避免从库恰好正在切换时踩空。2.2 我是否还依赖这些日志做时间点恢复binlog 的第二个核心作用是 PITR。如果你有一套每日全量备份备份完成之后产生的所有 binlog理论上都是恢复窗口的一部分。要是你把昨天全量备份之后的 binlog 全删了今天上午误删了一张表想恢复到误删前一瞬间你会发现中间少了好几个小时的数据恢复根本做不完整。常见的恢复窗口设计有两种。按时间窗假设业务允许最多丢 24 小时数据那么 binlog 至少保留 24 小时配合每日全量备份恢复点在 24 小时前。按事务容量假设一天产生 20GB 变更但你只想留 10GB binlog那恢复点只能在半天前这通常不够稳妥。我建议生产环境至少保留最近 3 天到 7 天的 binlog且这期间要有对应的全量备份。MySQL 的自动清理参数也好PURGE BEFORE 也好都必须以这个保留窗口为准。不要为了省那几十 GB 磁盘把一个保证数据可恢复的防线给拆了。2.3 删除动作和清空全部之间的安全距离这里必须分清楚两个动作PURGE 和 RESET MASTER。PURGE BINARY LOGS是删除一部分日志。它可以按文件号删到某个文件之前也可以按时间删到某个时刻之前但永远不会把当前正在写入的日志文件删掉也不会把从库正在读取的文件强制删掉。这是一个相对安全的范围操作。RESET MASTER则会删除所有 binlog 文件并把索引文件重置日志编号从 mysql-bin.000001 重新开始。这意味着所有从库的复制位点失效、之前所有 binlog 都不可用通常只在两种情况下使用初始搭建复制环境、或想彻底重置集群的日志轨道。生产环境如果没有十足把握不要碰它。我见过不少新手把删除 binlog理解成 RESET MASTER执行完看到从库报错、备份任务失败才意识到事情闹大了。所以这句话你值得抄下来线上清理 binlog优先用 PURGE BINARY LOGS绝不轻易用 RESET MASTER。3. 手动清理的正确姿势PURGE和它的两个兄弟理解了安全边界我们来看具体操作。日常高频操作主要是 PURGE配合自动清理参数基本能覆盖绝大多数运维需求。这一节我直接把命令语法、参数含义、注意事项都列出来。3.1 PURGE BINARY LOGS是你最常用的命令手动清理有两种写法按日志文件截止点删按时间截止点删。写法一删到指定日志文件之前保留 000009 及其之后PURGE BINARY LOGS TO mysql-bin.000009;执行后mysql-bin.000001 到 mysql-bin.000008 会被删除000009 及后续文件保留。写法二按时间删除某个时间点之前的日志PURGE BINARY LOGS BEFORE 2024-06-01 00:00:00;这条语句会用给定的时间与 binlog 文件的生成时间比较把所有生成时间早于该时刻的文件删掉。使用 PURGE 时有几个点容易踩。需要权限执行用户必须拥有BINLOG_ADMIN或SUPER权限否则报 Access denied。时间参数使用的是服务器本地时区如果数据库用 UTC、应用层用 Asia/Shanghai你按2024-06-01 00:00:00清理实际范围可能和你想的差 8 小时。它不会删除当前正在使用的 binlog 文件如果索引里只有 000010 且正在写你执行PURGE TO mysql-bin.000010实际上可能什么都不删因为 000010 是当前活跃文件MySQL 会保留。执行完可以再次SHOW BINARY LOGS确认文件列表再用df -h看磁盘空间是否释放。这是最标准的验证闭环。3.2 定期自动清理expire_logs_days与binlog_expire_logs_seconds手动 PURGE 适合临时处理长效方案还得靠自动清理参数。老版本5.x里的配置是expire_logs_days设置日志过期天数。比如[mysqld] expire_logs_days 7表示 binlog 最多保留 7 天超过 7 天的会在条件满足时被自动清理。MySQL 8.0 开始引入更精细的binlog_expire_logs_seconds以秒为单位设置过期时间默认值是 2592000也就是 30 天。两者的关系是如果binlog_expire_logs_seconds没设置会回退读取expire_logs_days如果两个都设置了MySQL 8.0 会警告expire_logs_days已废弃以binlog_expire_logs_seconds为准。在你升级到 8.0 后建议直接统一管理binlog_expire_logs_seconds。动态修改可以这样SET GLOBAL binlog_expire_logs_seconds 7 * 24 * 3600;注意GLOBAL 变量只对后续新产生的日志生效已存在但还没到过期时间的文件不会被立即删除需要等时间到达后在下一次轮转 binlog 时触发清理。如果想持久化记得把参数写进 my.cnf。补充一点有人说设置了自动清理就不会有目录堆积问题这话不完全对。自动清理的触发时机不是严格的定时任务而是在写入 binlog 时做过期检查。如果业务低峰期连续几小时没有新的 binlog 写入老日志即使过了保留期也可能迟迟不清理这就是为什么很多团队的清理任务还会补一个每周手工 PURGE。3.3 非必要不RESET MASTER除非你想重建复制RESET MASTER 是一条核弹级命令语法很简单RESET MASTER;执行后会删除所有 binlog并重新生成一个空的 mysql-bin.000001。当你的业务有主从复制时所有从库的复制位点全部失效从库必须重新 change master。什么场景会用到它全新搭建一套主从时为了让从库从一个干净的 000001 开始拉取某个环境数据彻底乱掉想回到初始化状态重建复制测试环境频繁重置。这些场景都有一个共同前提你不需要旧日志做恢复也不介意重建所有从库。我看到有些人在生产环境为了释放空间执行 RESET MASTER结果从库全部需要重建整个应用停摆。所以一句话总结RESET MASTER 是重置工具不是清理工具清理 binlog第一选择永远是 PURGE。4. 文件系统层面的真相为什么rm删了磁盘却没变小有些人图省事直接在 Linux 里 rm 掉 binlog 文件磁盘空间居然没变小而且 MySQL 日志开始刷报错。这个现象背后有三个知识点搞懂了你就不会再去盲删文件。4.1 rmmysql-bin.index以及句柄占用先说句柄占用。用一个不恰当的比喻MySQL 写 binlog 相当于在文件上挂了一把正在使用的锁。即使你用 rm 把文件从目录里移除只要 MySQL 进程还持有这个文件的文件描述符文件所在磁盘块就不会真正释放。这种文件被占用、通过 rm 删除的 inode在 Linux 里可以用 lsof 查看lsof | grep deleted | grep mysql如果看到很多 binlog 处于 deleted 状态恭喜你这些文件其实还占着磁盘空间。要等 MySQL 关闭句柄通常是刷新日志或重启后空间才会释放。所以 rm binlog 不仅没用还会让 MySQL 的索引文件和实际文件系统不一致后续一旦重启或执行FLUSH LOGSMySQL 找不到对应文件会直接报错。mysql-bin.index文件记录了当前 MySQL 认为存在的所有 binlog 文件名。当你 rm 掉某个文件但 index 里还留着它的名字MySQL 复制线程尝试定位它时就会失败错误信息跟 File not found 或者 Could not find target log 相关。因此千万别用 rm 删 binlog。如果真的一时手快删了第一步是通过 lsof 找到 deleted 状态的句柄再想办法用官方方式修复——不过修复 index 文件是一件非常繁琐且容易出错的事我建议直接优先考虑用备份恢复节点而不是手工去改 index。4.2 PURGE之后空间什么时候真正归还PURGE 是 MySQL 层正规的删除方式它在删除文件的同时会更新 mysql-bin.index并且会关闭自己已经持有的文件句柄。所以正常情况下PURGE 执行完磁盘空间会立刻释放。但存在一个例外如果某个 binlog 文件正被从库的 io_thread 读取主库的 PURGE 会根据复制状态自动保留正在使用的文件。也就是说即使你PURGE BEFORE一个很早的时间点MySQL 也不会删除某个从库仍在拉取的日志文件它会一直留到从库读完下一个文件为止。所以你可能遇到这种情况执行完 PURGE 后磁盘只释放了一部分还有几个文件明明时间很旧却还在。这不是 PURGE 失效了而是你的从库还挂在那些旧文件上。等从库追上来再执行一次 PURGE或者等自动清理逻辑把这些文件清除。这里也解释了为什么大批量删除 binlog 后如果你希望立刻看到效果最好选在从库没有积压的时段执行。否则清理到一半被从库拽住你会误以为命令没生效于是反复执行白白增加无谓的 IO。4.3 查看index文件判断目录里有没有幽灵日志维护 binlog 目录建议养成一个习惯定期对比实际目录里的文件数和 index 文件里的条目数。SHOW BINARY LOGS;这会读取索引并列出 MySQL 认可的 binlog 列表。如果你用 ls 看到目录里有某文件但SHOW BINARY LOGS不显示它说明它已经不在索引里是多余的幽灵文件。反过来SHOW BINARY LOGS里出现了文件而 ls 找不到那就麻烦了属于 index 内容和磁盘不一致通常需要停库修复或者直接用 mysqlbinlog 测试恢复。有个比较可靠的巡检命令是ls -l /var/lib/mysql/mysql-bin.* | wc -l wc -l /var/lib/mysql/mysql-bin.index两个数字应该一致。不一致就说明索引和实际文件错位建议在低峰期做修整。修整索引文件有一定的风险没有十足把握宁可先停写业务也不要乱改。5. 生产环境清理策略从我的一次磁盘告警说起前面讲了原理这一节我复盘一次真实的生产清理经历。这个场景很典型磁盘告警、从库延迟、业务还没停三位一体。如果你也碰到类似情况可以对照着看尽量少走弯路。5.1 告警现场100G binlog和正在堆积的从库延迟某个周五下午监控系统弹了一堆磁盘空间不足的告警主库数据目录磁盘使用率到了 94%。我登录上去看了几个关键数据SHOW BINARY LOGS; SHOW MASTER STATUS;mysql-bin 文件一共 200 多个累计约 120GB而整块数据盘总共才 300GB全量备份文件还在目录里占了 80GB磁盘被逼到了边缘。更头疼的是从库那边延迟已经到 20 分钟还在持续增长。我没有马上动手清理而是先问了自己两个问题为什么自动清理没触发为什么从库延迟了查参数发现binlog_expire_logs_seconds没有在配置文件里设置但 global 值是 0这是之前某次维护时被手动改过把自动清理关掉了。从库延迟则是因为主库有个报表的大查询拖慢了整体复制进程——但其实更大的原因可能是磁盘 IO 已经打满因为 binlog 太多写盘和读取彼此争抢。5.2 我当时的处理步骤和验证方法当时的处理顺序是这样的你可以直接抄作业先看从库状态确定最小安全性。执行SHOW SLAVE STATUS记录所有从库中读取位点最落后的Master_Log_File。我这边最落后的从库停在 mysql-bin.000156。检查主库正在写的文件。SHOW MASTER STATUS确认当前正在写 mysql-bin.000203。制定保留边界。因为业务要求 PITR 恢复窗口是 24 小时而 mysql-bin.000156 大约是 30 小时前的文件所以首选保留到 mysql-bin.000157再往前的全部清理。这里要注意从库在 000156 之后也没读过所以PURGE TO mysql-bin.000157会让 000157 之前的文件被尝试删除但 MySQL 会因从库需求自动保留至少 000156。按规范执行然后观察。执行 PURGEPURGE BINARY LOGS TO mysql-bin.000157;执行完后SHOW BINARY LOGS确认保留的文件列表再用df -h看空间大约释放了 90GB磁盘使用率从 94% 降到 78%。验证从库。再次检查从库状态发现它仍然能继续从 000156 或者 000157 拉取没有报错。延迟因为磁盘 IO 缓解后面逐步追平。补充自动清理参数避免再次出现。我把binlog_expire_logs_seconds改成 24 小时并写入了 my.cnf同时把原本的 0 值修正掉。这里提醒一下如果参数是 0自动清理是关闭的有的版本里 0 代表不允许自动过期效果一样都是不清理。这个流程最核心的经验是不要一上来就删先确认从库的位置再确认保留窗口。我当时还额外做了一件事在清理前后分别记录磁盘使用量和SHOW BINARY LOGS输出方便出问题时能快速判断是哪一步导致异常。这个习惯看起来笨但真到了审计或者回溯的时候能省下大量时间。5.3 自动清理参数设置的三个易错点好不容易清理完如果参数设置不对三周后又得重复一次。我在配置自动清理时总结出三个易错点希望你能避开。时区陷阱。PURGE BINARY LOGS BEFORE的时间参数用的是 MySQL 服务器时区。如果你写了2024-06-01 00:00:00但服务器时区是 UTC你就得把它当成 UTC 时间来理解。建议在维护脚本里先SET time_zone08:00明确时区再执行 PURGE。expire_logs_days和binlog_expire_logs_seconds同时设置的优先级。MySQL 8.0 在两者都设置时会以秒为单位生效并打印一条 warning 说expire_logs_days已经废弃。不要以为配了 days 就高枕无忧如果你的迁移脚本还在写expire_logs_days7可能没起到预期作用。自动清理不等于高水位令牌。它只在写 binlog 时检查如果连续好几个小时没有新日志产生垃圾文件可能在过期后迟迟不消失。所以我的保留策略是自动清理兜底每周一个定时 PURGE 强制清理一次双保险。6. 那些年我踩过的坑三条真实教训再好的理论都不如自己踩过的坑刻骨铭心。最后分享三个我真实遇到过的坑每一个都让我花了不少时间去善后。希望你看到这一段的时候能把它们当成前车之鉴。6.1 把expire_logs_days设为0自动清理直接失效有一次接手一个老环境看到配置文件里写着expire_logs_days0。我当时想0 就表示立即过期清理任务应该很激进。结果一个月后磁盘爆了查了资料才明白在这个参数里0 代表禁用自动清理。也就是说如果想让日志保留 N 天应该设置正整数设为 0 的唯一合理场景是我完全不需要自动清理。这个坑非常经典因为人的直觉会把 0 理解成清除所有但数据库配置常常把 0 当作关闭开关。排查的时候只要执行SELECT global.expire_logs_days, global.binlog_expire_logs_seconds;看到 0 或 NULL就要警惕自动清理是否被关闭。如果你在某个环境里不确定配置可以先用SHOW VARIABLES LIKE expire%和SHOW VARIABLES LIKE binlog_expire%把当前值打出来直观得多。6.2 RESET MASTER之后从库彻底失联第一次在生产环境处理主从问题时为了重置日志我在主库执行了 RESET MASTER。执行后主库的日志编号马上变成 000001但两个从库的 Change master 信息还停留在旧的位点。从库复制状态直接变成 No报错 1236定位不到主库 binlog。最后只能重新对从库做全量备份恢复再 change master花了将近两个小时才拉通所有节点。从那以后我对所有同事只有一个要求线上绝不允许 RESET MASTER如果需要清理只能 PURGE。如果有人坚持要重置复制那也要先在从库记录当前位点然后重建全部从库并且确认业务可以接受恢复窗口归零。这个坑属于典型的命令两三秒善后两小时能不碰就不碰。6.3 大事务写入期间执行PURGE意外变慢还有一次我在业务高峰期执行PURGE BINARY LOGS BEFORE结果原本几秒钟的清理跑了将近 20 分钟而且主库 IO 有明显波动。后来分析发现当时有个大事务正在生成上 GB 的 binlogPURGE 需要删除的文件非常多同时触发多次文件轮转和索引更新再加上从库正在读取相关文件MySQL 在内部还要做引用检查整体就被拖慢了。这给我的教训是大范围清理 binlog尽量安排在业务低峰期如果确需白天操作可以把 PURGE 的区间缩小比如先按文件数量拆成多段每次 PURGE 一小部分避免长时间占用 IO。批量清理时一条 PURGE 语句要处理的文件数不要超过几十个数量大了就分批删。最后再补充一个小技巧如果你实在需要快速释放空间并且能接受短时间的复制延迟可以临时暂停从库复制等 PURGE 完成再恢复但这只适用于主从完全健康、且你有完善兜底预案的情况不要轻易模仿。我个人在实际操作中的体会是binlog 清理这事不怕慢怕的是图快。每次动手前把从库位点、保留窗口、自动清理参数这三样都过一遍比任何命令都重要。别等到磁盘告警再看这篇文章平时就把清理策略配好远比事后救火舒服得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询