是基于查询原子的触发器?

时间:2013-10-21 22:02:33

标签: mysql triggers

我有一个包含序列号的表。此序列号将更改,并且引用自动编号将不起作用。我担心触发器的值会发生碰撞。如果两个交易同时读取。

我已经在3个连接上运行了模拟测试,每个记录约100万条,没有碰撞。

CREATE TABLE `aut` (
  `au_id` int(10) NOT NULL AUTO_INCREMENT,
  `au_control` int(10) DEFAULT NULL,
  `au_name` varchar(50) DEFAULT NULL,
  `did` int(10) DEFAULT NULL,
  PRIMARY KEY (`au_id`),
  KEY `Did` (`did`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

TRIGGER `binc_control` BEFORE INSERT ON `aut` 
FOR EACH ROW BEGIN
SET NEW.AU_CONTROL = (SELECT COUNT(did)+1 FROM aut WHERE did = NEW.did);
END;

4 个答案:

答案 0 :(得分:3)

是的,如果两个会话同时运行触发器,则会受到竞争条件的影响。你不应该使用这个解决方案。

在测试过程中可能不会发生,但您可以假设在生产过程中发生。 : - )

有句老话,One in a million is next Tuesday

答案 1 :(得分:0)

技术上,是的。它是原子的,初始语句加上所有副作用都是作为同一个事务提交的。

但是,根据隔离级别,查询的任何内容都可能出错。如果内存服务,MySQL在目录中有一个缓存值,但如果你在并发事务中使用非可序列化隔离读取它,这个缓存可能会变得陈旧。

答案 2 :(得分:0)

即使在显式的隔离事务中,仍然存在竞争条件,您可以最终获得重复的au_control值。 count(*)对性能来说太可怕了。如果这是你想要做的事情,有更好的方法来获得单调增加的指导。

答案 3 :(得分:0)

MySQL UUID_SHORT()函数可能对您有用。它是原子的,每次调用时都会生成一个ever_incrementing BIGINT UNSIGNED值,但要求您不要停止服务器,将时钟向后设置为前一个启动时间或接近上一个启动时间(取决于您调用它的频率)并再次启动服务器,或将@@server_id的值更改为LSB较低的值。

http://dev.mysql.com/doc/refman/5.6/en/miscellaneous-functions.html#function_uuid-short