为什么在这种情况下会出现MySQL数据库死锁?

时间:2017-04-07 02:01:37

标签: mysql locking deadlock

我在这里找到了一个类似的问题Deadlock using SELECT ... FOR UPDATE in MySQL。但答案并没有真正解释为什么会发生这种情况。

这种情况很容易重现@ Mysql 5.7.17(或5.5或5.6中的其他版本):

CREATE TABLE `test` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `val` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `search` (`val`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

insert into test set val='pre-lock';

==会话1 ==

start transaction;

select * from test where val='pre-lock' for update;

==会话2 ==

start transaction;

select * from test where val='pre-lock' for update;

==会话1 ==

insert into test set val='/a/b/c'; //note that if the set val='pre-lock111', there will be no deadlock at session 2

==会话2 ==

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

show engine innodb status的结果:

LATEST DETECTED DEADLOCK
------------------------
2017-04-06 23:54:03 0x7000057db000
*** (1) TRANSACTION:
TRANSACTION 1333, ACTIVE 18 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 5, OS thread handle 123145394155520, query id 62 localhost root Sending data
select * from test where val='pre-lock' for update
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 72 index search of table `test_tnx`.`test` trx id 1333 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 8; hex 7072652d6c6f636b; asc pre-lock;;
 1: len 8; hex 8000000000000001; asc         ;;

*** (2) TRANSACTION:
TRANSACTION 1332, ACTIVE 29 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 1
MySQL thread id 62, OS thread handle 123145394434048, query id 63 localhost root update
insert into test set val='/a/b/c'
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 24 page no 4 n bits 72 index search of table `test_tnx`.`test` trx id 1332 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 8; hex 7072652d6c6f636b; asc pre-lock;;
 1: len 8; hex 8000000000000001; asc         ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 24 page no 4 n bits 72 index search of table `test_tnx`.`test` trx id 1332 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 8; hex 7072652d6c6f636b; asc pre-lock;;
 1: len 8; hex 8000000000000001; asc         ;;

*** WE ROLL BACK TRANSACTION (1)

我的看法是会话1中的insert子句获得了一个间隙锁定,它通过select for update以某种方式与间隙锁定重叠。因此,这会造成僵局。但是,我找不到任何支持我的想法的文件。请帮忙解释一下。

7日更新1。 2017年4月:

我的完整用例如下。每个会话都启动一个事务。它首先锁定“预锁定”行以获得唯一性,然后执行一组插入。然后,它提交所有插入并释放锁定。据我所知,如果我删除“预锁定”,两个事务可能会导致死锁,并且两个都失败(mysql杀死一个,让另一个按照@Michael - sqlbot指出的那样去)。同时,允许其他一些会话读取之前已提交的表的内容,因此,独占锁定表不是提示。

7日更新2。 2017年4月:

要完全解释我的目标,这是一个很长的故事。简而言之,我想使用mysql作为外部系统的锁定服务。我提到的这些插入是一组外部锁。由于我有读/写锁,并且想要支持通配符,所以它不能是唯一的。在这些插入之前,我实际上需要使用一组选择进行冲突分析。设置全局预锁定对我来说是一个很好的解决方案来完成这项任务。

0 个答案:

没有答案