【发布时间】:2014-06-30 04:56:17
【问题描述】:
我的 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状态下的两个事务都是从相同的代码生成的,但只有其中一个事务(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
在调用第一个插入之后,会话 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
【问题讨论】:
-
InnoDB 处于哪种锁定模式?如果您处于多粒度锁定状态,这可能是额外死锁的来源。 dev.mysql.com/doc/refman/5.6/en/innodb-lock-modes.html
-
除了 innodb 状态外,我没有什么可做的。看起来事务 (2) 在模式 X 中持有锁,事务 (1) 和 (2) 都希望在模式 X 中以插入意图锁定。我假设你想要别的东西,因为这两个都在我原始问题的输出中。你能更具体一点,或者给我一个你想要的例子吗?
-
我的意思是......如何在 my.ini(和朋友)中配置 InnoDB?您的日志似乎暗示表和记录锁都处于活动状态。你能打开一个或另一个再试一次吗?您绝对需要这两个级别吗?
标签: mysql sql innodb deadlock database-deadlocks