【发布时间】:2015-01-27 10:40:23
【问题描述】:
我有与here 描述的相同的问题,而且我认为https://stackoverflow.com/a/22343265/297487 的答案是一个很好的解决方案,但我对此答案还有另一个问题。
以下触发器(从答案复制)是线程安全的吗?我的意思是如果将两条并发记录插入到表中,“优先级”列(如问题中所述)是否具有一致的值(与 id 相同的值)?
delimiter //
drop trigger if exists bi_table_name //
create trigger bi_table_name before insert on table_name
for each row begin
set @auto_id := ( SELECT AUTO_INCREMENT
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='table_name'
AND TABLE_SCHEMA=DATABASE() );
set new.priority= @auto_id;
end;
//
delimiter ;
我们如何解释“for each row”子句? 假设 MySQL 想要插入两个并发行。这个触发器是如何工作的?
一种解释如下:
MySQL 锁定表并在插入其中一行(并发记录之一)之前 MySQL 触发器启动并获取当前的 AUTO_INCREMENT 值并将其设置为“优先级”列,然后插入一条记录。之后 MySQL 开始插入另一条记录,然后同样的情况适用于新记录。
另一种解释可能如下:
当两条并发记录插入 MySQL 时,MySQL 锁定表,然后在插入两条并发记录之前启动触发器,“for each row”子句在两条记录之间迭代并将“优先级”列值设置为相同的值,然后在数据库中插入两条并发记录。在这种情况下,触发器无法按预期工作。
以上哪种解释是正确的?
更新:
我有下表:
CREATE TABLE `t_file` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_persian_ci NOT NULL,
`p_name` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `p_name_2` (`p_name`)
) ENGINE=InnoDB AUTO_INCREMENT=206284 DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci;
我想在插入行时插入与id 相同的p_name 值。
设置p_name 值的trigger 如下(从您的代码中复制)
delimiter $$
drop trigger if exists file_p_name $$
create trigger file_p_name before insert on t_file
for each row begin
set @id := ( SELECT AUTO_INCREMENT
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='t_file'
AND TABLE_SCHEMA=DATABASE() );
set new.p_name= @id;
end;
$$
delimiter ;
在我们的应用程序中,我将插入t_file 表的代码用try catch 包围起来,几乎总是一切正常,但有时(在并发插入t_file 表时),我在我们的表中看到以下异常应用程序的日志:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry \'387456\' for key \'p_name_2\''
看来trigger 没有按预期工作,或者我错了!!!
【问题讨论】: