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

为什么我在MySQL中出现死锁

昝浩阔
2023-03-14

我的MySQL表出现死锁。只涉及一个表,我可以始终如一地复制它。只有当我有多个线程运行代码时才会发生这种情况。

下面是表格:

CREATE TABLE `users_roles` (
  `role_id` bigint(20) NOT NULL,
  `user_id` bigint(20) NOT NULL,
  `created` datetime NOT NULL,
  PRIMARY KEY (`user_id`,`role_id`),
  KEY `created` (`created`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

然后,我在每个线程中运行这2个查询,每个线程具有不同的user_id值。

BEGIN;
DELETE FROM `users_roles` WHERE user_id = X;
INSERT INTO `users_roles` VALUES (7, X, NOW()); -- DEADLOCK ON THIS QUERY
COMMIT;

需要注意的是,当调用DELETE语句时,数据库中从不存在user_id X。正在使用运行这些查询的代码位创建新用户。然而,该函数允许我修改用户的帐户,因此,从旧用户的团队中删除现有的角色。

所以,当足够多的查询并行运行时,我就开始出现死锁。InnoDB状态的死锁部分在每次死锁后都会显示这一点。

------------------------
LATEST DETECTED DEADLOCK
------------------------
2014-05-09 16:02:20 7fbc99e5f700

*** (1) TRANSACTION:
TRANSACTION 6241424274, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 6 lock struct(s), heap size 1248, 3 row lock(s), undo log entries 6
MySQL thread id 3772090, OS thread handle 0x7fbc1f451700, query id 4010665755 10.0.141.36 1403_users update

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1331 page no 10665 n bits 192 index `PRIMARY` of table `users_data`.`users_roles` trx id 6241424274 lock_mode X insert intention waiting

*** (2) TRANSACTION:
TRANSACTION 6241424275, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
6 lock struct(s), heap size 1248, 3 row lock(s), undo log entries 6
MySQL thread id 3770297, OS thread handle 0x7fbc99e5f700, query id 4010665767 10.0.137.28 1403_users update
INSERT INTO users_roles(role_id, user_id, created) values(5, 102228093, NOW()) ON DUPLICATE KEY UPDATE user_id=user_id

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1331 page no 10665 n bits 192 index `PRIMARY` of table `users_data`.`users_roles` trx id 6241424275 lock_mode X

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1331 page no 10665 n bits 192 index `PRIMARY` of table `users_data`.`users_roles` trx id 6241424275 lock_mode X insert intention waiting

*** WE ROLL BACK TRANSACTION (2)

至于调试,或者尝试找出问题的实际所在,我已经能够通过从代码中删除DELETE语句来消除所有死锁。虽然这确实解决了这个问题,但我想理解它。

我理解的是MySQL处理间隙锁的方式。我知道它们在这个问题中起作用,因为当我执行DELETE语句时,行不存在。我不明白的是,为什么两个innodb状态的事务都是从相同的代码生成的,但只有其中一个transaction(2)有排他锁。这就好像事务(1)甚至没有尝试获得排他锁(没有插入意图)。

假设锁是正确的,我就能理解为什么会发生死锁:事务(2)获得排他锁,事务(1)请求插入意图,然后事务(2)请求插入意图。这是有道理的。没有意义的是事务(1)中没有排他锁(没有插入意图)。

编辑:

我能够用一个特定的命令顺序来再现这一点。

下面是表格:

CREATE TABLE `a` (
  `id` tinyint(3) unsigned NOT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

以下是问题。打开4个终端到mysql并按此顺序执行查询。

session 1: BEGIN;
session 2: BEGIN;
session 3: BEGIN;
session 4: BEGIN;
session 1: DELETE FROM `a` WHERE `id` = 5;
session 2: DELETE FROM `a` WHERE `id` = 10;
session 3: DELETE FROM `a` WHERE `id` = 7;
session 4: DELETE FROM `a` WHERE `id` = 12;
session 1: INSERT INTO `a` VALUES (5, 1);
session 2: INSERT INTO `a` VALUES (10, 1); -- deadlock here
session 3: INSERT INTO `a` VALUES (7, 1); -- deadlock here
session 4: INSERT INTO `a` VALUES (12, 1); -- deadlock here

下面是InnoDB状态,紧接在任何插入之前。

------------
TRANSACTIONS
------------
Trx id counter 11396965
Purge done for trx's n:o < 11396913 undo n:o < 0 state: running but idle
History list length 1248
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 11396962, ACTIVE 9 sec
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 3425, OS thread handle 0x7fcd14197700, query id 29686 localhost dev cleaning up
TABLE LOCK table `matthew`.`a` trx id 11396962 lock mode IX
RECORD LOCKS space id 7291 page no 3 n bits 80 index `PRIMARY` of table `matthew`.`a` trx id 11396962 lock_mode X
---TRANSACTION 11396961, ACTIVE 10 sec
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 3426, OS thread handle 0x7fccda225700, query id 29673 localhost dev cleaning up
TABLE LOCK table `matthew`.`a` trx id 11396961 lock mode IX
RECORD LOCKS space id 7291 page no 3 n bits 80 index `PRIMARY` of table `matthew`.`a` trx id 11396961 lock_mode X
---TRANSACTION 11396960, ACTIVE 11 sec
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 3391, OS thread handle 0x7fccd4d7f700, query id 29672 localhost dev cleaning up
TABLE LOCK table `matthew`.`a` trx id 11396960 lock mode IX
RECORD LOCKS space id 7291 page no 3 n bits 80 index `PRIMARY` of table `matthew`.`a` trx id 11396960 lock_mode X
---TRANSACTION 11396959, ACTIVE 13 sec
2 lock struct(s), heap size 376, 1 row lock(s)
MySQL thread id 3392, OS thread handle 0x7fccd4bf9700, query id 29671 localhost dev cleaning up
TABLE LOCK table `matthew`.`a` trx id 11396959 lock mode IX
RECORD LOCKS space id 7291 page no 3 n bits 80 index `PRIMARY` of table `matthew`.`a` trx id 11396959 lock_mode X

调用第一个insert后,会话1的终端将挂起锁。InnoDB状态显示:

---TRANSACTION 11396959, ACTIVE 841 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 376, 2 row lock(s)
MySQL thread id 3392, OS thread handle 0x7fccd4bf9700, query id 30234 localhost dev update
------- TRX HAS BEEN WAITING 32 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 7291 page no 3 n bits 80 index `PRIMARY` of table `matthew`.`a` trx id 11396959 lock_mode X insert intention waiting
------------------
TABLE LOCK table `matthew`.`a` trx id 11396959 lock mode IX
RECORD LOCKS space id 7291 page no 3 n bits 80 index `PRIMARY` of table `matthew`.`a` trx id 11396959 lock_mode X
RECORD LOCKS space id 7291 page no 3 n bits 80 index `PRIMARY` of table `matthew`.`a` trx id 11396959 lock_mode X insert intention waiting

共有1个答案

祝允晨
2023-03-14

我相信我找到了问题所在。

摘自http://dev.mysql.com/doc/refman/5.6/en/innodb-locks-set.html:

在插入行之前,设置一种称为插入意图间隙锁的间隙锁类型。这个锁以这样一种方式发出插入意图的信号,即插入到相同索引间隙中的多个事务如果不是在间隙内的相同位置插入,则不需要彼此等待。假设存在值为4和7的索引记录。分别尝试插入值5和6的事务在获得插入行的独占锁之前分别用insert INTERATION锁锁定4和7之间的空隙,但不会相互阻塞,因为行不冲突。

如果发生重复键错误,将设置重复索引记录上的共享锁。如果有多个会话尝试插入同一行,而另一个会话已经具有独占锁,则共享锁的使用可能会导致死锁。如果另一个会话删除该行,则可能发生这种情况。

这对我的案子意味着什么。

由于删除不影响行,所以它们都在表尾间隙上获得了一个共享锁(模式IX)。一旦执行了insert,共享锁仍然被所有线程持有,insert意图等待这个共享锁的释放。

解决方案是不并行执行以下操作:

  1. 删除要插入的行,当行不在时。
  2. 插入行

所以InnoDB引擎的状态是错误的。它无法显示每个事务都持有相同的锁。它未能显示每个锁是lock_mode IX,而不是x,它未能显示每个线程也有一个等待授予的插入意图锁。总之,show ENGINE INNODB status;出现了一个非常严重的故障。

 类似资料:
  • 问题内容: 我在以下一行中得到一个: 我正在打印所有值: 输出: 那我为什么要得到呢?请帮助。谢谢。完整的堆栈跟踪如下: 问题答案: 简单的答案:您在该位置没有得到NullpointerException- 至少不是根据您发布的stacktrace。Liferay的LoginAction没有原始的StrutsAction。如果您使用的是6.2 GA5,则这是引发NullpointerExcepti

  • 问题内容: 为什么下面的代码抛出ConcurrentModificationException?JoshBloch可以避免ConcurrentModificationException。 问题答案: 使用“ for each”循环时,不能在列表上使用remove。相反,您可以使用此方法在迭代器上调用remove: 如果您实际上想用“ 200”代替每个值,或用其他值代替,则建立一个新列表可能更有意义

  • 我正在尝试使用命令,但收到的回报是错误: 而且我很确定数据库中并不存在该表,因为数据库完全是空的和干净的。 我使用的是PHP 7.3和MySQL 8.0.18社区版,它们都是在Windows Server 2019上由AMPPS运行的。我用的是Laravel 8

  • 问题内容: 我的应用程序在Linux上作为后台进程运行。当前在“终端”窗口的命令行中启动。 最近,一个用户执行该应用程序一段时间后,它神秘地死了。文本: 被杀 在航站楼上。这发生了两次。我问其他终端是否有人使用kill命令杀死进程?没有。 Linux在什么情况下会决定终止我的进程?我相信外壳程序显示为“ killed”,因为该进程在收到kill(9)信号后就死了。如果Linux发送了kill信号,

  • core.js:1671错误类型错误:generatorOrNext不是SafeSubscriber.SchedulerFn[as_next](core.js:3565)在SafeSubscriber.push../node_modules/rxjs/_esm5/internal/subscriber.js.safeSubscriber.__tryRunsub(subscriber.js:195)

  • 问题内容: 我们注意到,用C#(或Java)开发的软件中的许多错误都导致NullReferenceException。 为什么在语言中甚至包含了“ null”? 毕竟,如果没有“ null”,那么我就不会有错误,对吧? 换句话说,如果没有null,该语言的什么功能将无法正常工作? 问题答案: “ C#父亲” Anders Hejlsberg在他的《计算机世界》采访中谈到了这一点: 例如,在类型系统