statement模式下主库执行一次、从库重放时再触发一次,造成双重执行;row模式虽不重放触发器,但遇now()、uuid()等非确定性函数会隐式降级为statement,埋下不一致隐患。

STATEMENT模式下主库执行一次、从库再触发一次
这是双重执行最典型的成因。在binlog_format = STATEMENT时,主库只记录原始SQL(如INSERT INTO orders),不记录触发器是否被调用、调用几次、产生了什么副作用。从库重放这条语句时,会“重新走一遍”整个执行流程——包括触发器逻辑本身。
常见错误现象包括:
- 主库插入1行,从库多出2行日志(
orders_log表里重复写入) -
UUID()或NOW()在主库生成一个值,从库又生成一个新值,导致主从字段内容不一致 - 触发器里
SELECT ... FROM other_table查到的是从库当前快照,可能为空或过期数据
ROW模式下看似安全,但非确定性函数仍会引发隐式STATEMENT回退
binlog_format = ROW本应避免二次触发,因为只记录行变更结果,不重放逻辑。但MySQL发现触发器中含NOW()、UUID()、USER()、子查询带LIMIT等“不可确定”操作时,会自动降级为STATEMENT格式记录该事件——你根本不会收到任何警告,却已在binlog里埋下双重执行隐患。
验证方式:
- 在主库执行
SHOW BINLOG EVENTS IN 'mysql-bin.000001' LIMIT 20 - 若看到混有
Query_log_event(STATEMENT)和Table_map_log_event(ROW),说明已发生隐式切换
从库上手动创建触发器 + read_only = OFF = 主动打开双重执行开关
有人误以为“只要在从库也建个触发器,就能补上主库漏掉的逻辑”。这等于主动开启双重执行通道,且必须同时满足三个危险条件:
-
read_only = OFF:否则触发器内INSERT/UPDATE直接报ERROR 1290 (HY000) - 从库已手动执行
CREATE TRIGGER,且DEFINER用户存在、有TRIGGER权限 -
binlog_format = STATEMENT:只有这个模式下,从库才会真的再次触发——但代价是主从行为彻底不可控
此时主库写入一次,从库不仅重放原始语句,还额外执行自己的触发器,等于同一业务逻辑被执行两次,数据状态必然错乱。
最容易被忽略的点:没有slave_run_triggers这个配置项
网上流传的“设SET GLOBAL slave_run_triggers = ON让从库执行触发器”,纯属误解。MySQL官方文档和源码中从未定义该变量,执行会报ERROR 1238 (HY000): Variable 'slave_run_triggers' is a read only variable或直接提示未知变量。
真正决定是否“双重执行”的,只有两件事:
- 主库
binlog_format取值(STATEMENT / ROW / MIXED) - 触发器是否实际部署在从库表上(且具备执行条件)
别在参数上浪费时间,重点检查你的触发器里有没有NOW()、UUID()、跨表SELECT,以及binlog格式是否真的稳定为ROW——这些才是实际咬人的地方。











