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
如果我进行以下任一更改,就不会出现此问题。
- 删除PHP代码中包含的交易
- 从 PHP 代码
中删除 $this->insertRecord('audit_table'); 行
- 修改触发器,使其没有来自 audit_table
的 select 语句
我想知道这个问题的根本原因。 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?如果是,为什么?
我发现了一个 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
如果我进行以下任一更改,就不会出现此问题。
- 删除PHP代码中包含的交易
- 从 PHP 代码 中删除 $this->insertRecord('audit_table'); 行
- 修改触发器,使其没有来自 audit_table 的 select 语句
我想知道这个问题的根本原因。 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?如果是,为什么?