MySQL主从复制针对trigger的特殊处理

今天在测试MySQL的主从复制时,发现了关于trigger的一个有意思的现象,那就是trigger在从库似乎不起作用。

(1)、测试案例

create table mm(id int, name varchar(100));

insert into mm values(2,'xxxx');

 

create table mm_trig like mm;

 

delimiter //

create trigger simple_trig

after update

on mm for each row

begin

insert into mm_trig values(1,'s');

end;

//

delimiter ;

主库上创建一张测试表mm,并且针对该表创建一个trigger,如果对mm测试表进行update操作,就向另外一张表插入一条记录。

(2)、在主库上对mm测试表进行update操作,之后的结果如下所示。

root@localhost [course]>select * from mm;

+------+------+

| id | name |

+------+------+

| 2 | v |

+------+------+

1 row in set (0.00 sec)

 

root@localhost [course]>select * from mm_trig;

+------+------+

| id | name |

+------+------+

| 1 | s |

+------+------+

1 rows in set (0.00 sec)

 

root@localhost [course]>

从库上,这两张测试表的结果与主库完全一致。

(3)、这个测试结果与我想象中完全不一样,基于对GoldenGate工具的使用经验,此时的mm_trig测试表中,理论上应该有两条记录才对。主库针对mm测试表的这个update操作,由于trigger的作用,还间接地发起了对mm_trig的insert操作。使用mysqlbinlog工具解析主库的binlog日志也可以看出这个update操作和insert操作。

# at 667

#210818 5:56:07 server id 131 end_log_pos 706 Write_rows: table id 109 flags: STMT_END_F

### UPDATE `course`.`mm`

### WHERE

### @1=2 /* INT meta=0 nullable=1 is_null=0 */

### @2='xxxx' /* VARSTRING(400) meta=400 nullable=1 is_null=0 */

### SET

### @1=2 /* INT meta=0 nullable=1 is_null=0 */

### @2='v' /* VARSTRING(400) meta=400 nullable=1 is_null=0 */

### INSERT INTO `course`.`mm_trig`

### SET

### @1=1 /* INT meta=0 nullable=1 is_null=0 */

### @2='s' /* VARSTRING(400) meta=400 nullable=1 is_null=0 */

# at 706

如果是GoldenGate复制,GoldenGate的复制进程会在从库上执行这个update操作和insert操作,此时mm_trig测试表中会存在一条记录,又由于从库上存在这个trigger,所以trigger又会触发一次insert操作。也即mm_trig测试表中会存在两条记录。GoldneGate为了避免这种数据不一样的问题,提出了两种解决方案:1. 从库上不创建这个trigger,或者将这个trigger置于失效状态;2. GoldneGate工具设置相关的参数,在底层禁止trigger触发。

(4)、分析从库上生成的binlog日志,只能看到从主库上同步过来的这个update和insert操作,而没有从库上trigger起作用后的日志。这说明trigger在从库上基本没有生效,没有做任何特殊的设置,但从库上的trigger却不生效,这个问题非常疑惑。

(5)、最终,在MySQL的官方文档中,找到了这段话:

忘了交代了,我的测试环境正是基于row-based的复制,原来是故意这样设计的。如果是基于statement-based的复制,就需要注意trigger在从库也会执行。