为什么两个并发的删除+插入语句会在空表上死锁?

drs*_*der 5 mysql innodb database-deadlocks

我很好奇为什么当主键不存在时,两个使用主键的并发语句会导致 MySQL 死锁DELETEINSERT该示例旨在以最简单的形式说明该问题。

这是设置。

> SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
+-------------------------+------------------+
| @@GLOBAL.tx_isolation   | @@tx_isolation   |
|-------------------------+------------------|
| REPEATABLE-READ         | REPEATABLE-READ  |
+-------------------------+------------------+
1 row in set
Time: 0.002s

> select version();
+-------------+
| version()   |
|-------------|
| 5.7.12      |
+-------------+
1 row in set
Time: 0.002s

create table lock_test ( id int(11) not null, primary key (`id`) );
Run Code Online (Sandbox Code Playgroud)

下面,1>代表一个mysql终端,2>代表另一个终端。

1> begin;
1> delete from lock_test where id = 1;

2> begin;
2> delete from lock_test where id = 2;

1> insert into lock_test values (1); -- hangs

2> insert into lock_test values (2);
*** deadlock ***
Run Code Online (Sandbox Code Playgroud)

这是show engine innodb status输出:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2018-06-06 16:15:18 0x70000ba52000
*** (1) TRANSACTION:
TRANSACTION 807765, ACTIVE 46 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 620, OS thread handle 123145496289280, query id 43097 localhost ::1 root update
insert into lock_test values (1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 6819 page no 3 n bits 72 index PRIMARY of table `content_graph`.`lock_test` trx id 807765 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) TRANSACTION:
TRANSACTION 807766, ACTIVE 37 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 617, OS thread handle 123145497681920, query id 43099 localhost ::1 root update
insert into lock_test values (2)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 6819 page no 3 n bits 72 index PRIMARY of table `content_graph`.`lock_test` trx id 807766 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;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 6819 page no 3 n bits 72 index PRIMARY of table `content_graph`.`lock_test` trx id 807766 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** WE ROLL BACK TRANSACTION (2)
Run Code Online (Sandbox Code Playgroud)

请注意,如果您首先插入 ID 为 1 和 2 的记录,然后重复上面的顺序,则不会出现死锁。

我的感觉是,因为密钥不在索引中(两者都在附加),所以删除必须锁定更多索引(主要将进入的页面的更多部分),但我想确保我的理解正确。

Ric*_*mes 2

“间隙”被锁定,因为预期有人可能会尝试插入我要删除的行。

或者,换个角度来看……完美处理每一个奇怪的情况都太慢了。因此,InnoDB 选择有效地处理大多数情况,并把赌注押在罕见的奇怪情况上。

底线:忍受它。你陷入僵局。你不一定能够理解它们。但是您的代码需要恢复 - 只需回滚并返回到BEGIN.