SQLSTATE[40001]: Serialization failure: 1213 Deadlock issue caused by INSERT 触发器并发访问

SQLSTATE[40001]: Serialization failure: 1213 Deadlock issue caused by INSERT trigger on concurrent access

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

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上有一个触发器,定义如下。它所做的是 select 从 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 函数简单地向给定的 table.

插入一条记录
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

如果我进行以下任一更改,就不会出现此问题。

我想知道这个问题的根本原因。 MySQL 触发器被触发时,另一个事务是否从它开始?触发器中的事务和锁是如何工作的?

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

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

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 提供):发生这种情况是因为 known issue in MySQL. Setting a variable from a select acquires a lock when using read uncommitted isolation level. This thread 有更多信息。

您是否正在使用隔离模式 Read Uncommitted?如果是,为什么?