SQLSTATE [40001]:序列化失败:1213在并发访问时由INSERT触发器引起的死锁问题

时间:2019-10-31 06:41:55

标签: mysql concurrency triggers transactions innodb

我发现一个SQL死锁问题,该问题由两个用户同时执行时发生。我有一个PHP函数,该函数执行包含在事务中的几个数据库插入查询。其中一个插件也会触发触发器。请参阅下面的表格结构和代码示例。

main_table

CREATE TABLE `main_table` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `action_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

history_table

CREATE TABLE `history_table` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `action_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  `audit_id` INT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

audit_table

CREATE TABLE `audit_table` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `action_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我在 main_table 上有一个触发器,定义如下。它的作用是从 audit_table 中选择最大id并将一条记录插入 history_table

CREATE TRIGGER watch_insert_main_table
AFTER INSERT ON main_table
FOR EACH ROW BEGIN
    DECLARE max_id INT;
    SET max_id = (SELECT MAX(`id`) FROM `audit_table`) + 1;
    INSERT INTO `history_table` SET audit_id=max_id;
END;

以下是由两个用户同时执行的功能。 insertRecord 功能只是将记录插入给定表中。

try {
    $dbConnection->beginTransaction();
    $this->insertRecord('main_table');    
    $this->insertRecord('audit_table');
    $dbConnection->commit();    
} catch (Exception $e) {
    $dbConnection->rollback();    
}

第二次(同时)调用该函数时,出现以下死锁错误。

40001 - SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction

如果我进行以下更改之一,则不会发生此问题。

  • 删除PHP代码中包含的事务
  • 从PHP代码中删除 $ this-> insertRecord('audit_table');
  • 修改触发器,使其没有来自audit_table的select语句

我想知道此问题的根本原因。触发MySQL触发器时​​是否正在启动另一个事务?触发器中的事务和锁如何工作?

我发现以下两个问题也与相似的问题有关。

1 个答案:

答案 0 :(得分:0)

看看这种简化是否有帮助:

CREATE TRIGGER watch_insert_main_table
AFTER INSERT ON main_table
FOR EACH ROW BEGIN
    INSERT INTO `history_table` (audit_id)
        SELECT MAX(`id`)+1 FROM `audit_table`;
END;

请注意它将两个语句组合为一个语句的方式。 可以避免让另一个连接进入那里并抓住相同的id(或类似的东西)。

可能与以下 相关(由OP提供):发生这种情况是由于MySQL中的known issue。使用读取未提交隔离级别时,通过选择设置变量将获得锁定。 This thread有更多信息。

您是否使用隔离模式“读取未提交”?如果是这样,为什么?