【问题标题】:Is this MySQL trigger thread-safe?这个 MySQL 触发器线程安全吗?
【发布时间】: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 没有按预期工作,或者我错了!!!

【问题讨论】:

    标签: mysql triggers


    【解决方案1】:

    answer referred in your question 是我不久前发布的。

    "For Each Row" 中,NEW对应的新行,在上下文中被插入。 set new.xxx 仅适用于该行,但不适用于 batch 中的 'for all rows'。所以不会有任何碰撞,也不会有失败的问题。

    我也回答了一个类似的问题 How does “for each row” work in triggers in mysql?。请仔细阅读答案中给出的示例。

    参考文档

    【讨论】:

    • 你的好像没问题,但是我把这段代码上传到了一个有很多并发用户的相同情况的系统中。我会通知你结果。
    • 我为了在并发系统中测试上述触发器,我为“优先级”列创建了一个唯一约束。三天后,我看到一些异常表明重复的“优先级”列插入提到表。所以看起来上面的触发器不是线程安全的,并且在并发系统中存在一些问题。
    • 很糟糕,MySQL 没有“后触发器”在插入后更新一行。
    • 我不同意你的看法。您提到您将priority 创建为unique 列。如果通过trigger 定义对列进行更新并且失败,则触发器不是这里的罪魁祸首,而是在触发器主体中设置约束列值的想法。如果触发器不是线程安全的,则它们可能不存在。如果您仍然相信您测试的内容是正确的,您最好发布操作顺序以及表、触发器定义和数据。
    • 对不起,延迟回答。我正在评估我的代码以确保异常与trigger有关。我更新了我的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-17
    • 1970-01-01
    相关资源
    最近更新 更多