
遇到 Binlog has a corruption 错误,MySQL 通常已拒绝启动或复制中断
这个错误不是警告,是硬性拒绝——MySQL 在解析 binlog 文件时校验失败(比如 magic number 不对、event header 损坏、checksum 验证失败),直接 abort。常见于磁盘故障、强制断电、mysqlbinlog 手动编辑后保存、或使用了不兼容的压缩/传输方式处理 binlog 文件。
修复前先确认:你是否还有未应用的 relay log?是否启用了 binlog_checksum(5.6.6+ 默认 ON)?损坏的是 active binlog 还是历史归档?这些决定你能走哪条路。
- 如果损坏的是当前正在写的
mysql-bin.0000xx(即SHOW MASTER STATUS返回的那个文件),且没有备份,基本无法安全恢复——跳过它会导致主从数据不一致,强行 reset master 会丢事务 - 如果损坏的是旧 binlog(比如
mysql-bin.000012,而当前是.000025),且你不需要做 PITR(基于时间点恢复)或补从库,可直接删除该文件 + 刷新日志(FLUSH BINARY LOGS) - 若启用了
binlog_checksum = CRC32,但 MySQL 启动时报 checksum mismatch,可能是配置被降级覆盖(例如从 8.0 降回 5.7 但没关 checksum),此时需统一版本或显式设为NONE
用 mysqlbinlog --base64-output=DECODE-ROWS -v 定位损坏位置
别直接打开 binlog 文件看——它是二进制格式。用 mysqlbinlog 解析能暴露具体出错的 offset 和 event 类型,这是后续操作的前提。
执行命令时加 --force-read 可跳过单个损坏 event 继续解析(仅用于诊断,不可用于恢复):
mysqlbinlog --base64-output=DECODE-ROWS -v --force-read /var/lib/mysql/mysql-bin.000017
输出中一旦出现类似 ERROR: Error in Log_event::read_log_event(): 'read error', data_len: 123, event_type: 30,就说明损坏发生在该位置附近。注意记录下报错前最后一个合法的 GTID_LOG_EVENT 或 QUERY_EVENT 的 position(比如 # at 12345)。
- 不要用文本编辑器(如 vim)直接删改 binlog 文件——哪怕只删一行,整个文件结构就全废
-
--force-read是“尽力而为”,不代表能完整读完;若连第一个 event 都解析失败,大概率文件头部损坏,基本无修复价值 - MySQL 8.0+ 若开启
binlog_transaction_compression = ON,需额外加--binlog-transaction-compression参数才能正确解压解析
主库 binlog 损坏但从库完好:优先重建主库 binlog,而非硬修
线上最稳妥的做法,往往不是“修”,而是“绕”——利用从库的完整数据重置主库 binlog 状态,避免在损坏日志上做任何写操作。
前提是:从库 IO_THREAD 和 SQL_THREAD 均已停止,且 Seconds_Behind_Master 为 0,Relay_Master_Log_File 和 Exec_Master_Log_Pos 指向一个确定的主库 binlog 位置。
- 在从库执行
STOP SLAVE; SHOW SLAVE STATUS\G,记下Master_Host、Relay_Master_Log_File(如mysql-bin.000020)、Exec_Master_Log_Pos(如1987) - 在主库停服,备份剩余可用 binlog(如
mysql-bin.000020到.000024),然后RESET MASTER—— 这会清空mysql-bin.index并新建mysql-bin.000001 - 再用
CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000020', MASTER_LOG_POS=1987把从库指向原位置(注意:此时主库已无该文件,所以必须立刻START SLAVE IO_THREAD让从库把缺失的 events 补传回来) - 该过程依赖从库的 relay log 完整性;若 relay log 也损坏,则只能靠最近一次逻辑备份 + 后续 binlog 回放(前提是你有没损坏的 binlog)
实在要手动截断 binlog,SET GLOBAL sql_log_bin = OFF 不起作用
很多人想“跳过损坏段”,尝试关 binlog 再执行 FLUSH LOGS,但这是无效的——sql_log_bin 只控制当前 session 是否写入 binlog,不影响 MySQL 启动时对现有 binlog 文件的校验流程。
真正能绕过校验的,只有两个低层手段(风险极高,仅限测试环境):
- 用
dd截掉损坏部分(例如从报错 position 开始 truncate):dd if=/dev/null of=/var/lib/mysql/mysql-bin.000017 bs=1 seek=12345 conv=notrunc—— 但后续所有 position 偏移都会错乱,MySQL 极可能拒绝加载 - 用
hexedit手动修复 magic header(前 4 字节应为0xfe 0x62 0x69 0x6e)或 event length 字段——需要完全理解 binlog format v4 结构,一比特写错就全盘崩溃 - MySQL 官方不提供任何 binlog 修复工具;Percona Toolkit 中的
pt-table-checksum和pt-table-sync可校验/修复数据一致性,但不碰 binlog 文件本身
真实生产中,binlog 文件一旦被标记为 corruption,它的可信度就归零了。比“怎么修”更重要的是:检查磁盘健康(smartctl)、确认备份链是否完整、以及下次是否启用 binlog_checksum = CRC32 并定期用 mysqlbinlog --verify-binlog-checksum 抽检。










