必须用ROW模式,否则主从不一致是必然爆发的问题;STATEMENT模式下NOW()、UUID()、RAND()、USER()、@variable、无ORDER BY的LIMIT、未声明DETERMINISTIC的函数或触发器均必然导致主从结果不同。

必须用 ROW 模式,否则主从数据不一致不是“会不会发生”的问题,而是“什么时候爆发”的问题。
STATEMENT 模式下哪些操作必然导致主从不一致
它不靠逻辑推演,只看函数签名和语法结构。只要语句里出现以下任一元素,主库和从库执行结果就注定不同:
-
NOW()、CURDATE()、SYSDATE():主库写入时间戳是2026-09-30 11:28:03,从库重放时可能是11:28:06,差值直接写进字段 -
UUID()、RAND()、USER():主库生成的 UUID 是550e8400-e29b-41d4-a716-446655440000,从库再执行一次就是另一个值 -
@variable用户变量:赋值顺序、会话隔离、初始化状态在主从间无法对齐 -
LIMIT无ORDER BY的删除或更新:InnoDB 行物理顺序不保证,主从删的极可能是完全不同的行集 - 未声明
DETERMINISTIC的自定义函数或触发器:MySQL 不敢假设行为一致,但也不会报错,静默分叉
ROW 模式到底记录什么、怎么验证它生效了
ROW 不记 SQL,只记「哪张表、哪几行、哪个字段、旧值是什么、新值是什么」。这意味着同步过程彻底脱离执行上下文,纯靠数据快照驱动。
查看日志必须加两个参数:mysqlbinlog --base64-output=decode-rows --verbose,否则看到的是不可读的 base64 字符串。
实操注意点:
- 一条
UPDATE product SET price = 99 WHERE id IN (1,2,3)会生成 3 条独立的Update_rows_log_event,不是 1 条语句事件 -
DDL(如ALTER TABLE)仍是明文,别误判为“日志不可读” - 无主键表在 ROW 下复制会严重延迟:MySQL 需全表扫描匹配条件行,无法走索引定位
- 确认当前格式:运行
SELECT @@binlog_format;,别信默认值;MySQL 5.7+ 虽默认ROW,但老实例或配置覆盖后可能回退
切到 ROW 后为什么从库突然报 Error_code: 1032
这不是 ROW 模式的问题,而是暴露了早已存在的主从数据不一致。ROW 模式要求“行存在才能改”,而 STATEMENT 模式只要 SQL 能执行就过——哪怕改的是错的行。
典型诱因:
- 切换前从库已落后,主库在 STATEMENT 模式下写了含
NOW()的记录,从库执行后时间偏移,后续 ROW 日志找不到那行 - 主库切模式时仍有活跃写入连接,
SET GLOBAL binlog_format = 'ROW'只影响新会话,老连接继续写 STATEMENT 日志,造成混合 binlog -
binlog_row_image设为MINIMAL或NOBLOB,从库缺少足够字段还原变更,尤其涉及 BLOB/TEXT 或部分列更新时
修复不能靠 sql_slave_skip_counter 跳过,得先用 SHOW SLAVE STATUS\G 提取缺失表名和主键条件,在主库查出原始行,手动补到从库。
MIXED 模式真能自动兜底吗
不能。它的判定规则是 MySQL 内置硬编码表,不理解你的业务语义。比如:
-
INSERT INTO t SELECT RAND() * 100 FROM dual,子查询可能未被识别为非确定性上下文,仍走 STATEMENT - 你写了
UPDATE t SET x = @a := @a + 1,MySQL 看到用户变量就强制切 ROW——哪怕这句只是本地调试,根本没走复制链路 - 新版本函数或 UDF 若未在规则表中登记,MySQL 判定不准,可能漏切,也可能误切
所以 MIXED 不是“智能降级”,而是“有边界的自动 fallback”。它适合历史包袱重、短期无法清理非确定性 SQL 的系统,但新项目上线就该直接设为 ROW。
真正容易被忽略的点是:binlog_format 只控制 DML,而 binlog_row_image 和 binlog_transaction_dependency_tracking 这些配套参数不调好,ROW 模式照样出问题。尤其是后者,关系到并行复制能否安全启用——不配好,切了 ROW 也发挥不出优势。











