Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/58.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么MySQL中会出现死锁_Mysql_Sql_Innodb_Deadlock_Database Deadlocks - Fatal编程技术网

为什么MySQL中会出现死锁

为什么MySQL中会出现死锁,mysql,sql,innodb,deadlock,database-deadlocks,Mysql,Sql,Innodb,Deadlock,Database Deadlocks,我的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`

我的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;
CREATE TABLE `a` (
  `id` tinyint(3) unsigned NOT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
然后,我在每个线程中运行这两个查询,每个线程都有一个不同的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语句时,数据库中从不存在用户_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状态下的两个事务都是由相同的代码生成的,但其中只有一个事务(2)具有独占锁。这就好像事务(1)甚至没有尝试获取独占锁(没有插入意图)

假设锁是正确的,我可以理解为什么会出现死锁:事务(2)获得独占锁,事务(1)请求插入意图,然后事务(2)请求插入意图。这是有道理的。没有意义的是事务(1)中没有独占锁(没有插入意图)


编辑:

我能够用特定的命令顺序重现这一点

这是表格:

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;
CREATE TABLE `a` (
  `id` tinyint(3) unsigned NOT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
以下是问题。向mysql打开4个终端,按此顺序执行查询

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

我相信我已经找到了问题所在

发件人:

在插入行之前,将设置一种称为插入意图间隙锁的间隙锁。此锁表示插入的意图,如果插入到同一索引间隙中的多个事务没有插入到间隙中的同一位置,则它们不需要等待对方。假设存在值为4和7的索引记录。尝试插入值5和6的单独事务在获得插入行的独占锁之前,每个事务都使用插入意图锁锁定4和7之间的间隙,但不会相互阻止,因为这些行不冲突

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

这对我来说意味着什么

删除操作,因为它们不影响任何行,所以在表间隙的末尾都有一个共享锁(模式IX)。一旦执行了插入,共享锁仍然由所有线程持有,并且插入意图等待该共享锁的释放

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

  • 当不存在行时,删除要插入的行
  • 插入行

  • 因此,InnoDB引擎状态是错误的。它无法显示每个事务都持有相同的锁。它无法显示每个锁都是lock_mode IX,而不是X。它无法显示每个线程也有一个等待授予的插入意图锁。总之,
    显示引擎INNODB状态是一个相当惊人的失败

    InnoDB处于哪种锁定模式?如果您处于多粒度锁定中,那么这可能是额外死锁的来源。除了innodb状态之外,我没有什么可抱怨的。看起来事务(2)在模式X中持有锁,事务(1)和(2)都希望在模式X中具有插入意图的锁。我假设你还想要别的东西,因为这两个都在我原始问题的输出中。你能更具体一点,或者给我一个你想要的例子吗?我的意思是。。。InnoDB在my.ini(和friends)中是如何配置的?您的日志似乎暗示表锁和记录锁都处于活动状态。你能打开其中一个然后再试一次吗?你真的需要两个级别吗?我认为“X意向等待”==IX。我同意你的看法,迈克尔。那些没有“意图等待”的锁让我很困惑,因为它们应该显示为共享锁或IX锁。那些显示有“意向等待”的人显然就是这样。