最直接办法是登录从库执行select @@sql_log_bin;返回1表示当前会话写binlog、触发器默认执行,返回0则不写binlog但触发器仍可能运行;再通过show create trigger分别比对主从定义、sql security、字符集及delimiter,并确认@@binlog_format均为row且无非确定性函数。

确认从库是否实际执行了触发器
最直接的办法是登录从库,查 SELECT @@sql_log_bin; —— 如果返回 1,说明当前 session 会写 binlog,也意味着触发器默认会被执行(除非显式禁用);返回 0 则当前 session 不写 binlog,但触发器仍可能运行(只要没被 DROP 或权限拦截)。别只看配置文件,运行时值才作数。
检查触发器定义是否主从一致
用 SHOW CREATE TRIGGER trigger_name; 分别在主库和从库执行,逐字比对输出。重点核对:
-
DEFINER用户是否存在且有TRIGGER权限(从库缺用户会导致加载失败,SHOW TRIGGERS可能不显示) -
SQL SECURITY是DEFINER还是INVOKER(DEFINER下从库会以定义者身份执行,权限不足就静默失败或报错) - 字符集、分隔符(
DELIMITER)、注释内容是否完全一致(某些版本会因注释差异导致解析失败)
验证 binlog_format 是否真正统一为 ROW
不能只信配置文件或 my.cnf,必须查运行时值:
- 主库执行:
SELECT @@binlog_format;,必须是'ROW' - 从库执行同样命令,也必须是
'ROW';若为'STATEMENT'或'MIXED',触发器必然引发双写或漏写 -
MIXED尤其危险:MySQL 可能在任意时刻自动降级到STATEMENT,而触发器行为无法预测
改完后需确认所有从库已生效(重启或 SET GLOBAL binlog_format = 'ROW'),并观察 SHOW SLAVE STATUS\G 中的 Seconds_Behind_Master 是否突增——突增往往意味着触发器副作用开始暴露。
排查触发器内部是否调用了非确定性函数
即使用了 ROW 模式,触发器里写 UUID()、NOW()、SYS_DATE() 这类函数,主从结果仍会不一致。因为这些函数每次执行都生成新值,且依赖本地系统时间。
- 查触发器 SQL 体:
SHOW CREATE TRIGGER xxx后人工扫描,搜UUID(、NOW(、SYS_DATE( - 正确做法是:应用层生成唯一 ID 或时间戳后传入;或改用
NEW.field := DEFAULT(field)让 MySQL 自动填充(前提是字段有默认值) - 若必须用时间,确保主从系统时钟已用
chrony同步,误差 NOW() 差异会被放大
真正容易被忽略的是:触发器里看似“安全”的操作,比如更新同一张表的另一个字段,只要它依赖运行时上下文(哪怕只是 ROW_COUNT()),在从库重放时语义就可能偏移——ROW 模式只保证变更的行数据一致,不保证触发器执行逻辑的上下文一致。











