今天在测试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的官方文档中,找到了这段话:
16.4.1.34 Replication and Triggers With statement-based replication, triggers executed on the source also execute on the replica. With row-based replication, triggers executed on the source do not execute on the replica. Instead, the row changes on the source resulting from trigger execution are replicated and applied on the replica. This behavior is by design. If under row-based replication the replica applied the triggers as well as the row changes caused by them, the changes would in effect be applied twice on the replica, leading to different data on the source and the replica. |
忘了交代了,我的测试环境正是基于row-based的复制,原来是故意这样设计的。如果是基于statement-based的复制,就需要注意trigger在从库也会执行。