带有使用select max的触发器的事务。它返回错误的结果

时间:2017-06-04 00:44:17

标签: mysql transactions innodb mariadb

我有一个复杂的数据库。我可以这样简化:

表a:

CREATE TABLE a
(
Id int(10) unsigned NOT NULL AUTO_INCREMENT,
A int(11) DEFAULT NULL,
CalcUniqId int(11) DEFAULT NULL,
PRIMARY KEY (Id)     
) ENGINE=InnoDB;

CREATE TRIGGER a_before_ins_tr before INSERT on a
FOR EACH ROW
BEGIN
  select Max(CalcUniqId) from A into @MaxCalcUniqId;
  set new.CalcUniqId=IfNull(@MaxCalcUniqId,1)+1;
END $

它的工作原理如下:

start transaction
insert into A(A)
... insert in other tables. It take between 30 and 60 seconds
commit;

问题是,触发器为同时运行的所有事务返回相同的CalcUniqId。

是否有任何解决方案或工作。

这是一个解决方案:

start transaction;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
insert into A(A) values(10);
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
....
commit;

男人可以进行这个测试:
第一节:

Step1: start transaction;
Step2: insert into A(A) values(1);
Step3: commit;

第二节:

Step1: start transaction;
Step2: insert into A(A) values(2);
Step3: commit;

在会话1中运行步骤1,2,在会话2中运行步骤1,2。比两者中的第3步。之后呢

select Id, A, CalcUniqId from a;

两者都具有相同的CalcUniqId = 2.

1 个答案:

答案 0 :(得分:2)

将触发器中的SELECT更改为:

select Max(CalcUniqId) from A into @MaxCalcUniqId
    FOR UPDATE;   -- add this

告诉交易你打算改变价值;阻止其他交易改变它。

这可能会导致您的30-60秒交易一个接一个地运行。并且可能因超过lock_wait_timeout而死亡。请注意更大的图片,而不是增加设置(已经“太高”)。也许我们可以编写一个解决方法,使得“正确”值并行运行。