触发器在从库根本不执行,所谓“重复执行”是误解;根本原因是statement模式下触发器逻辑不写入binlog,从库仅重放原始语句,而row模式下虽记录数据变更但不重放触发器逻辑。

触发器在从库重复执行的根本原因
这不是“重复执行”,而是根本没执行——STATEMENT 模式下,触发器逻辑压根不进 binlog,从库只重放原始语句(如 INSERT INTO orders),对 UPDATE inventory 或 INSERT log_audit 这类触发器内部操作完全无感知。你看到的“主从数据不一致”,其实是从库漏执行,不是多执行。
为什么把 binlog_format 改成 ROW 还不行
改了配置但没生效,是常见假象。必须逐项确认:
-
SELECT @@binlog_format;在主库和从库都执行,两处都必须返回'ROW';仅主库设了没用 - 执行
SET GLOBAL binlog_format = 'ROW';后,已有连接(包括复制线程)仍用旧格式,必须重启 MySQL 或确保新连接建立后 SQL Thread 重连 - 检查
SELECT @@sql_log_bin;,若为0,说明当前会话或某段代码绕过了 binlog(如SET SQL_LOG_BIN = 0),ROW 模式也捕获不到 - 从库
slave_exec_mode必须是STRICT,IDEMPOTENT会静默忽略主键冲突,掩盖触发器未写入的事实
ROW 模式下仍出现不一致的三类硬伤
即使 binlog_format 正确,以下情况会导致主从行数据内容不同,本质是“主库写入的值”和“从库生成的值”不一致:
- 触发器里调用了
UUID()、NOW()、SYSDATE()、RAND()—— 这些函数在从库回放时重新求值,结果必然不同 - 触发器访问了用户变量(如
@tmp_id)或临时表 —— 从库没有对应会话上下文,直接跳过或报错 - 触发器内调用了含
SELECT ... INTO或游标的存储过程 —— ROW 不记录中间查询结果,只记最终 DML,逻辑链断裂
这类问题无法靠配置修复,必须重构:把 UUID() 移到应用层生成并作为字段传入;用 CURRENT_TIMESTAMP 替代 SYSDATE();拆掉临时表逻辑,改用 JOIN 或子查询。
如何验证触发器是否真正同步生效
别信 Seconds_Behind_Master = 0,要验证行为本身:
- 在主库插入一条能触发目标触发器的数据(比如向
orders插一条测试订单) - 立即查主库所有被触发器影响的表(如
inventory、log_audit),记录每条变更的字段值和时间戳 - 立刻切到从库,查相同记录的对应字段 —— 注意比对具体值,尤其是
NOW()写入的时间、USER()写入的操作人、自增 ID 是否连续 - 如果发现某张表没变化、时间字段为
NULL或默认值、COUNT(*)主从不等,说明触发器逻辑未落地,不是延迟问题,是根本没走通
最易被忽略的是:触发器定义本身没同步过去。务必在主从两端分别执行 SHOW CREATE TRIGGER trigger_name;,逐字符比对输出结果是否完全一致。











