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