一、故障原因
使用trigger在特定的场景(场景描述见Bug重现以及原因分析)下触发了mysql的bug,官方已经确认bug还没有修复,目前5.1以上版本都存在。
Bug地址http://bugs.mysql.com/bug.php?id=53079
二、解决方案
1.将binlog_format修改为mixed或者row-based(可以在线修改)。
2.从应用角度分析,摒弃trigger这种实现方式。
三、Bug重现以及原因分析
下面是从bug中摘取的,描述的很清晰,我这里就不做翻译了,已经进行了测试了和bug描述的一致。
Description:
When there is an UPDATE and an INSERT trigger on same table, and both triggers perform an
INSERT into a second table which has an auto_inc column, replication fails with a DUP KEY
error on the UPDATE statement if the transactions are committed in a different order than
the statements are executed. This is because the binary log contains a SET INSERT_ID
statement before the UPDATE, and that ID is used by the trigger (not by the UPDATE
statement itself).
How to repeat:
Create the following two tables and two triggers in a replicated environment, using
InnoDB engine and 5.1.45 distribution.
master [localhost] {msandbox} (test) > CREATE TABLE a
(
-> id
int(10) unsigned NOT NULL auto_increment,
-> c1
varchar(255) NOT NULL,
-> PRIMARY KEY (id
)
-> ) ENGINE=InnoDB ;
Query OK, 0 rows affected (0.00 sec)
master [localhost] {msandbox} (test) > CREATE TABLE b
(
-> id
int(10) unsigned NOT NULL auto_increment,
-> a_id
int(10) unsigned NOT NULL,
-> op
enum(‘i’,’u’,’d’) default ‘i’,
-> t
timestamp NOT NULL default CURRENT_TIMESTAMP,
-> PRIMARY KEY (id
)
-> ) ENGINE=InnoDB ;
Query OK, 0 rows affected (0.00 sec)
master [localhost] {msandbox} (test) > delimiter ;;
master [localhost] {msandbox} (test) > create trigger a_ai after insert on a for each row
begin insert into b (a_id, op) values (NEW.id, ‘i’); end;;
Query OK, 0 rows affected (0.01 sec)
master [localhost] {msandbox} (test) > create trigger a_au after update on a for each row
begin insert into b (a_id, op) values (NEW.id, ‘u’); end;;
Query OK, 0 rows affected (0.00 sec)
I will upload the full log of reproducing the bug in a file, but the gist of it is:
On master, open 2 connections and run insert and update on table a
in the following
order:
1: INSERT
1: COMMIT
2: BEGIN
2: UPDATE
1: BEGIN
1: INSERT
1: COMMIT
2: COMMIT
The interleaving of statements causes the order of statements in the binary log to differ
from the order in which the TRIGGERs executed on the master (the master executes IUI but
the slave executes IIU), and therefor the UPDATE fails with a DUPKEY error because the
binary log included a SET INSERT_ID before the UPDATE statement.
The slave will execute both inserts into the a
table, and the fail when it executes the
update, claiming a duplicate key error for primary key value “2”.
Suggested fix:
Do not include SET INSERT_ID before an UPDATE, even if that UPDATE fires a TRIGGER which
generates a new auto_inc value. That was the behavior in 5.0.87, and replication did not
break (though the master and slave did get out of sync).