ROW模式下触发器不会写入binlog,主库仅记录其产生的行变更事件(如Write_rows/Update_rows),从库回放时跳过触发逻辑直接应用数据变更。

触发器在ROW模式下是否还会写入binlog
不会。binlog_format=ROW 时,主库触发器只在本地执行一次,其产生的行变更(比如更新了某字段、插入了关联行)会被打包进 Write_rows_log_event 或 Update_rows_log_event 中,**不记录触发器本身**。从库回放时只应用这些行变更,完全跳过触发逻辑。所以你看到 SHOW TRIGGERS 在从库存在,不代表它被调用——那只是定义残留。
为什么触发器会导致binlog退化为STATEMENT格式
MySQL 在 ROW 模式下检测到“不可确定性”操作时,会自动降级为 STATEMENT 记录整条原始 SQL,连带触发器逻辑一起复制过去。典型诱因包括:
-
NOW()、UUID()、RAND()、USER()等函数调用 - 触发器内含
SELECT ... FOR UPDATE或访问未索引的大表 - 触发器修改了当前 DML 语句未涉及的其他表(比如 INSERT t1 触发后 INSERT t2)
- 触发器里用了子查询 +
LIMIT,或依赖 session 变量(如@var)
这种退化无声无息,但会导致 binlog 体积暴增、SQL Thread 单线程串行回放、延迟飙升。验证方式:在主库执行 SHOW BINLOG EVENTS LIMIT 20,若混有 Query_log_event,说明已退化。
如何确认触发器对复制的影响是否被消除
不能只看配置文件或 SHOW VARIABLES LIKE 'binlog_format',必须做三件事:
- 分别在主库和从库执行
SELECT @@binlog_format,确保都返回'ROW' - 检查主库是否被意外关闭 binlog:
SELECT @@sql_log_bin必须为1 - 查从库状态:
SHOW SLAVE STATUS\G中Seconds_Behind_Master是否稳定;若某次写入后该值突增,且Slave_SQL_Running_State显示Updating或Trigger,大概率是触发器拖慢了 SQL Thread
特别注意:即使开了 ROW,如果触发器修改了辅助表(如日志表),而该表在从库缺失或结构不一致,Could not execute Write_rows_v1 event 错误会直接中断复制。
比禁用触发器更实际的规避策略
硬删触发器不是唯一解法,关键是把副作用可控化:
- 把触发器逻辑“下沉”到应用层:比如订单插入后,由业务代码异步写审计日志,避免阻塞主库事务提交
- 若必须保触发器,限定它只操作当前表的字段(如自动生成
updated_at),绝不跨表、不调函数、不查数据 - 所有被触发器修改的表,必须有主键或唯一索引——否则从库定位不到目标行,复制会卡死
- 定期用
mysqldump --skip-triggers备份从库,防止触发器定义意外同步过去干扰判断
最常被忽略的一点:触发器本身不慢,但它让主库生成的 binlog 行变更更“重”——比如一条 UPDATE 触发了 5 行变更 + 2 行插入,ROW 格式就得写 7 条事件。这放大了网络传输和从库解析压力,延迟就藏在这种“隐形膨胀”里。


















