MySQL主从复制中,如果主库和从库的binlog_format取值不一致,且日志解析机制彼此不兼容,就会直接造成复制失败。比如主库写入的是ROW事件,而从库却用STATEMENT方式解析relay log,通常就会报出Error executing row event或Got fatal error 1236。此时,应分别在主库和从库执行SELECT @@binlog_format确认配置是否一致。若发现不一致,需要先STOP SLA VE,再执行SET GLOBAL binlog_format='ROW',并重启MySQL,确保SQL线程真正加载新配置。同时,还要进一步检查binlog_row_image=FULL、sla ve_exec_mode=STRICT以及@@sql_log_bin=1是否满足要求。

主库和从库binlog_format值不同直接导致同步失败
这并不是简单的配置未生效,而是主库与从库根本不在同一套binlog解析体系里。主库记录的是ROW事件,但从库却尝试以STATEMENT模式去解析relay log,这种情况下几乎一定会触发Error executing row event或Got fatal error 1236。最常见的现象是在执行SHOW SLA VE STATUS\G时,发现Sla ve_SQL_Running: No,并且Last_Error中会明确提示日志格式不兼容。
- 必须分别在主库和从库执行
SELECT @@binlog_format;,不能只检查主库,也不能仅依赖my.cnf配置文件判断 - 如果结果不一致(例如主库为
'ROW'、从库为'STATEMENT'),应立即停止从库复制:STOP SLA VE; - 随后在从库执行
SET GLOBAL binlog_format = 'ROW';,并重启MySQL服务,这比单纯修改GLOBAL变量更稳妥 - 同时确认从库的
sla ve_exec_mode为'STRICT'而不是'IDEMPOTENT',否则真实错误可能被隐藏
为什么SET GLOBAL binlog_format='ROW'后仍不生效
原因在于已有连接(包括复制SQL线程)不会自动切换到新的日志格式,它们仍然沿用启动时加载的旧值。即使你看到SELECT @@binlog_format返回'ROW',那也只是当前新连接的结果,并不能说明复制线程已经完成切换。
- 执行
STOP SLA VE;后再START SLA VE;,强制SQL线程重新建立连接 - 检查
SHOW SLA VE STATUSG中的Seconds_Behind_Master是否开始下降;如果一直停留在0,但Sla ve_SQL_Running依旧为No,说明SQL线程并未真正以新格式启动 - 更可靠的处理方式是直接重启从库MySQL进程,确保内部IO线程和SQL线程都按新配置重新初始化
- 也不要忽略
binlog_row_image参数——即便binlog_format已经设为ROW,如果它是MINIMAL,那么UPDATE/DELETE操作可能缺失关键字段,进而导致pt-table-checksum校验失败
混合模式下MIXED fallback到STATEMENT引发隐性不一致
binlog_format=MIXED并不是解决主从复制问题的安全方案。MySQL在判断语句是否“确定性”时存在盲区,尤其是在函数调用、触发器、子查询等场景下,可能会悄悄fallback回STATEMENT,而你往往不会收到明显警告。
- 可以在慢查询日志中搜索
UUID()、NOW()、@var、SYS_DATE(),一旦出现,通常说明某些语句被MIXED判定为“不确定”,从而走了STATEMENT路径 - 检查主库是否开启了
log_bin_trust_function_creators=0,该配置可能使自定义函数被强制按STATEMENT记录,即使函数声明了DETERMINISTIC - 像
ALTER TABLE后紧接INSERT这样的DDL与DML混合操作,在MIXED模式下非常容易触发fallback,直接统一切换到ROW通常更省心 - 在GTID模式下,STATEMENT与ROW事务不能混杂在同一个binlog文件中,否则IO线程可能直接中断并报错
验证ROW模式是否真正覆盖所有路径
修改配置只是第一步,更关键的是确认真实业务流量是否已经全部按ROW模式写入。尤其要关注运维脚本、备份工具、ETL任务等“非交互式连接”,因为这些连接很可能绕过你已经调整好的配置。
- 在主库执行
SELECT @@sql_log_bin;,结果必须为1;如果返回0,说明某些脚本执行了SET SQL_LOG_BIN = 0,这部分数据变更根本不会写入binlog - 使用
mysqlbinlog --base64-output=DECODE-ROWS -v解析最新binlog,确认事件类型是Write_rows/Update_rows,而不是Query事件 - 持续观察一个完整业务周期(例如订单高峰期8小时),确认从库
Seconds_Behind_Master没有持续增长,且Last_Errno始终保持为0 - 历史binlog仍会按切换前的旧格式保留,建议至少保存7天以上,便于后续回溯分析历史数据不一致的来源
真正棘手的往往不是修改binlog_format本身,而是那些根本没有进入binlog的变更,例如因sql_log_bin=0被跳过的操作,或被replicate-ignore-db过滤掉的库表。即使启用了ROW模式,这类数据也同样不会参与主从复制。
