row模式是唯一能可靠同步触发器和存储过程执行结果的binlog_format;statement模式仅记录语句而非实际数据变更,导致从库重放时因now()、临时表等上下文差异引发静默数据不一致。

ROW 模式是唯一能可靠同步触发器和存储过程执行结果的 binlog_format。 STATEMENT 模式下,从库重放的是原始 SQL,而触发器、存储过程、函数等在从库执行时可能因上下文(如 NOW()、USER()、临时表、会话变量)不同导致行为不一致;MIXED 模式虽自动降级,但对复杂逻辑仍不可控。
为什么 STATEMENT 模式会让触发器和存储过程同步失败
STATEMENT 模式只记录“执行了什么语句”,不记录“实际改了哪些数据”。当主库执行一条带触发器的 INSERT,binlog 里只存这条 INSERT;从库回放时,会再次触发该触发器——但此时触发器内部引用的 NOW()、@session_var、LAST_INSERT_ID() 或临时表状态,都可能与主库当时不同。
常见错误现象包括:
- 从库触发器插入时间戳比主库晚几秒甚至几分钟
- 存储过程中依赖
SELECT ... INTO @var的变量在从库为空或值错乱 - 触发器基于临时表做判断,而临时表在从库不存在或内容不一致
-
SHOW SLAVE STATUS中Seconds_Behind_Master正常,但业务数据已不一致(静默错误)
ROW 模式如何解决这个问题
ROW 模式不重放语句,而是直接记录“哪一行、哪个字段、从什么值变成什么值”。触发器和存储过程在主库执行后的最终行变更会被完整捕获,从库跳过触发逻辑,直接应用变更结果。
关键点:
- 主库上的触发器/存储过程只在主库执行一次,其副作用(如日志表写入、计数器更新)被固化为行事件
- 从库 SQL 线程解析
Write_rows、Update_rows、Delete_rowsevent,不做任何二次计算 - 需确保
binlog_row_image = FULL(默认),避免MINIMAL导致部分字段缺失引发解析失败 - 若使用 MySQL 5.7+,建议同时开启
binlog_transaction_dependency_tracking = WRITESET,提升并行复制稳定性
配置和验证要点
不要只改配置文件后重启就认为生效。必须确认当前 session 和 global 级别均为 ROW,且从库已按新格式接收日志:
- 主库执行:
SET GLOBAL binlog_format = 'ROW';(立即生效,无需重启) - 检查是否生效:
SELECT @@global.binlog_format;和SELECT @@session.binlog_format; - 主库执行一条带触发器的 INSERT 后,用
SHOW BINLOG EVENTS IN 'mysql-bin.0000xx' LIMIT 10;查看 event 类型是否为Write_rows - 从库执行
SHOW SLAVE STATUS\G,确认Relay_Log_Space在增长,且Exec_Master_Log_Pos持续推进 - 禁止在从库手动执行
SET sql_log_bin = 0后调用存储过程——这会导致该操作完全不记 binlog,主从彻底脱节
最容易被忽略的是:即使主库设为 ROW,如果从库在 CHANGE MASTER 时指定了旧的 binlog 文件位置(比如还在 STATEMENT 日志段内),它会继续读取旧格式日志直到追上——这时同步依然不可靠。务必在切换 binlog_format 后,用 FLUSH LOGS 切换新 binlog 文件,并在从库 CHANGE MASTER TO ... MASTER_LOG_FILE='mysql-bin.0000xx', MASTER_LOG_POS=xxx 指向新文件开头。











