我发现一个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
如果我进行以下更改之一,则不会发生此问题。
我想知道此问题的根本原因。触发MySQL触发器时是否正在启动另一个事务?触发器中的事务和锁如何工作?
我发现以下两个问题也与相似的问题有关。
答案 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有更多信息。
您是否使用隔离模式“读取未提交”?如果是这样,为什么?