前言
技术群早上有个兄台发了一个死锁的日志,我瞄了一眼,发现插入也会死锁,好奇让人变得更强大。
日志
看下那位老哥的日志
------------------------
LATEST DETECTED DEADLOCK
------------------------
2019-12-20 04:00:03 0x7f78e455e700
*** (1) TRANSACTION:
TRANSACTION 3460338, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 2011281, OS thread handle 140156593497856, query id 29253160 10.105.232.16 DBopr update
insert into TABLE_NAME
( IS_DELETED,
CREATOR,
MODIFIER,
GMT_MODIFIED,
GMT_CREATE,
POLICY_NO,
INS_COMPANY_CODE,
INSURED_CERT_NO,
INSURED_NAME,
INSURED_PHONE,
POLICY_STATUS,
IS_REGISTER,
VERSION,
POLICY_JSON,
EXT )
values ( 'n',
'system',
'system',
current_timestamp,
current_timestamp,
'8510101000551308',
'0004',
'39tIt45QY+OzmZ/eAHuEoJ0pTRuieyFLHq6xNR6taoY=',
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 956 page no 98724 n bits 520 index IDX_PN_ID of table `DB`.`TABLE_NAME` trx id 3460338 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 437 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 16; hex 38353130313031303030353531333238; asc 8510101000551328;;
1: len 1; hex 6e; asc n;;
2: len 8; hex 8000000000104ae2; asc J ;;
*** (2) TRANSACTION:
TRANSACTION 3460340, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 2024731, OS thread handle 140157203638016, query id 29253163 10.105.232.16 DBopr update
insert into TABLE_NAME
( IS_DELETED,
CREATOR,
MODIFIER,
GMT_MODIFIED,
GMT_CREATE,
POLICY_NO,
INS_COMPANY_CODE,
INSURED_CERT_NO,
INSURED_NAME,
INSURED_PHONE,
POLICY_STATUS,
IS_REGISTER,
VERSION,
POLICY_JSON,
EXT )
values ( 'n',
'system',
'system',
current_timestamp,
current_timestamp,
'8510101000551318',
'0004',
'l1cSOBesExUFjAZflVQ3dfWeQH8F/EMJEDgOJyvf5fY=',
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 956 page no 98724 n bits 520 index IDX_PN_ID of table `DB`.`TABLE_NAME` trx id 3460340 lock_mode X locks gap before rec
Record lock, heap no 437 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 16; hex 38353130313031303030353531333238; asc 8510101000551328;;
1: len 1; hex 6e; asc n;;
2: len 8; hex 8000000000104ae2; asc J ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 956 page no 98724 n bits 520 index IDX_PN_ID of table `DB`.`TABLE_NAME` trx id 3460340 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 437 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
0: len 16; hex 38353130313031303030353531333238; asc 8510101000551328;;
1: len 1; hex 6e; asc n;;
2: len 8; hex 8000000000104ae2; asc J ;;
*** WE ROLL BACK TRANSACTION (2)
有点长,而且比较难懂。
解释
第一个insert的时候idx索引上拿到x锁,lock_mode X locks gap before rec insert intention waiting
第二个insert的时候lock_mode X locks gap before rec,也是跟gap有关系
划重点
gap
看到上面总总情况,都跟gap有关系,那么我们就要了解下gap是什么东东。
个人理解:gap是意向锁,可以来解决幻读。为啥?你改完锁住,别人就不能改了,这样就不会幻读了。
官网解读
我们可以看到gap也分共享锁何排他锁,如果是一个范围的话,会锁一个范围的。如果可以指定到特定行,那么gap就是一个行的。
参考官网
insert导致死锁原因排查
参考网址
gap锁在insert里面起到作用
insert拿到共享的gap意向锁,然后再去拿X锁。在我看来,mysql根据gap去把控并发,而不是直接把MySQL锁去争抢。
重要的一点:如果出现重复键错误,则会在重复索引记录上设置一个共享锁。如果有多个会话试图插入同一行(如果另一个会话已经具有互斥锁),则使用共享锁可能会导致死锁。
我们再来理解一下上面这句话的意思:插入的时候会生成插入意图的gap锁,如果在不同范围不会互相阻塞。比如插入4,7跟插入5,6不会互相阻塞。但是一旦重复键重复冲突,则会在重复索引记录上设置一个共享锁。如果一个事务拿到共享锁,那么他们就都拿不到排他锁了。
重点
- 死锁的形成,一个事务拿了gap共享锁,也就是上面日志里面的idx索引里面的对应关系,这时它要去拿排他锁,凉了,排他和共享是不兼容的,阻塞住了。再看另一个事务,共享锁也没有拿到,排他锁也没有拿到,gg
- 为啥共享锁和排他锁不兼容?看下下面的图
来源:CSDN
作者:go大鸡腿
链接:https://blog.csdn.net/weixin_38336658/article/details/103669389