当前位置: 首页 > 知识库问答 >
问题:

SQLSTATE[40001]:序列化失败:并发访问时插入触发器导致1213死锁问题

姬存
2023-03-14

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

主表

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

历史表格

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;

审计表

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;

下面是由两个用户同时执行的函数。插入记录功能简单地将一条记录插入到给定的表中。

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代码中包含的事务

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

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

  • MySQL死锁与一个插入引发触发器
  • 由INSERT触发器引起的死锁-线程正在寻找它已经拥有的锁?

共有1个答案

暴夕
2023-03-14

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

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中的一个已知问题造成的。使用读取未提交隔离级别时,从select设置变量会获得锁。此线程包含更多信息。

您是否使用隔离模式读取未提交?若然,原因为何?

 类似资料:
  • 我得到以下错误当有多个更新运行特定的表数据库-mysql-innodb引擎语言php-pdo 错误-序列化失败:尝试获取锁时发现死锁 我检查了下面的链接SQLState[40001]:序列化失败:尝试获取锁时发现1213死锁;尝试按Magento位置重新启动事务

  • 我在调用StackExchange时遇到死锁情况。雷迪斯。 我不知道到底发生了什么,这非常令人沮丧,我将感谢任何有助于解决或解决这个问题的意见。 万一你也有这个问题,不想读这些东西;我建议您尝试将设置为。 这样做可能会解决这种死锁 该代码作为控制台应用程序或Azure Worker角色运行 它使用HttpMessageHandler公开RESTAPI,因此入口点是异步的 代码的某些部分具有线程关联

  • seata版本:1.4.0,但1.4以下的所有版本也都有这个问题 问题描述:在一个全局事务中,一个分支事务上的纯查询操作突然卡住了,没有任何反馈(日志/异常),直到消费端RPC超时 问题排查 整个流程在一个全局事务中,消费者和提供者可以看成是全局事务中的两个分支事务,消费者 --> 提供者 消费者先执行本地的一些逻辑,然后向提供者发送RPC请求,确定消费者发出了请求已经并且提供者接到了请求 提供者

  • 本文向大家介绍深入浅出解析mssql在高频,高并发访问时键查找死锁问题,包括了深入浅出解析mssql在高频,高并发访问时键查找死锁问题的使用技巧和注意事项,需要的朋友参考一下 死锁对于DBA或是数据库开发人员而言并不陌生,它的引发多种多样,一般而言,数据库应用的开发者在设计时都会有一定的考量进而尽量避免死锁的产生.但有时因为一些特殊应用场景如高频查询,高并发查询下由于数据库设计的潜在问题,一些不易

  • 我正在将一个INSERT写入一个DB2表,该表使用我插入的记录作为触发器,并被指控是锁定文件和阻止触发器工作的罪魁祸首。 目前,我可以编写记录,但它会挂起我的应用程序,并且我没有从DB2数据库得到任何响应。(尽管它确实写了!)。 我被告知我可能正在使用“更新模式”写作,但除了没有实际设置任何模式之外,我没有做任何具体的事情: 我只能猜测我的应用程序挂起的原因,其中一个猜测是没有在INSERT语句上

  • 主要内容:示例,死锁解决方案示例死锁描述了两个或多个线程等待彼此而被永久阻塞的情况。 当多个线程需要相同的锁定但以不同的顺序获取时,会发生死锁。 Java多线程程序可能会遇到死锁状况,因为关键字会导致执行线程在等待与指定对象相关联的锁定或监视时出现阻止情况。 看看下面一个例子。 示例 当您编译并执行上述程序时,会出现死锁情况,以下是程序生成的输出 - 上述程序将永久挂起,因为两个线程都不能继续进行,等待彼此释放锁定,所以您可以按