死锁摘录【yetdone】

1

https://www.cnblogs.com/gimin/p/5466247.html

尽量避免事务,缩小事务范围(避免gap锁)(个人认为)

按表顺序,记录id顺序依次访问

直接申请足够级别的锁,即排他锁,而不应先申请共享锁,更新时再申请排他锁,因为当用户申请排他锁时,其他事务可能又已经获得了相同记录的共享锁,从而造成锁冲突,甚至死锁。

在REPEATABLE-READ隔离级别下,如果两个线程同时对相同条件记录用SELECT...FOR UPDATE加排他锁,在没有符合该条件记录情况下,两个线程都会加锁成功。程序发现记录尚不存在,就试图插入一条新记录,如果两个线程都这么做,就会出 现死锁。这种情况下,将隔离级别改成READ COMMITTED,就可避免问题

如果出现死锁,可以用SHOW INNODB STATUS命令来确定最后一个死锁产生的原因。

2

https://www.cnblogs.com/aspirant/p/10572560.html

3.3不同索引锁冲突

     这种情况比较隐晦,事务A在执行时,除了在二级索引加锁外,还会在聚簇索引上加锁,在聚簇索引上加锁的顺序是[1,4,2,3,5],而事务B执行时,只在聚簇索引上加锁,加锁顺序是[1,2,3,4,5],这样就造成了死锁的可能性。

                                                                          

降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁——与上述死锁案例无关

3

https://my.oschina.net/mingletxt/blog/899362

mysql事务死锁案例分析(GAP锁篇) 

官方文档

下面是mysql对gap lock给出的官方解释:

A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record。

在官方详细解析中有提到一个要点,也是导致本次事务死锁的根本原因

Gap locks in InnoDB are “purely inhibitive”, which means they only stop other transactions from inserting to the gap. They do not prevent different transactions from taking gap locks on the same gap. Thus, a gap X-lock has the same effect as a gap S-lock.

gap lock不排斥其他gap lock,但排斥插入意向锁

大致意思是:X-lock的gap和S-lock的效果是一样的,都是可以被多个事务获取到

详见:https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-gap-locks

下面开始先分析gap lock 在唯一索引中的表现

以上为mysql中,innodb下,建立唯一索引,在执行sql中存在索引不命中时使用了gap锁导致的死锁场景分析,主要原因还是在于gap锁可以被多个事务获取到,谢谢拍砖^_^

4

https://my.oschina.net/hebaodan/blog/1835966

加锁分析

  1. delete的where子句没有满足条件的记录,而对于不存在的记录 并且在RR级别下,delete加锁类型为gap lock,gap lock之间是兼容的,所以两个事务都能成功执行delete;关于gap lock可以参考文章加锁分析。这里的gap范围是索引a列(3,5)的范围。
  2. insert时,其加锁过程为先在插入间隙上获取插入意向锁,插入数据后再获取插入行上的排它锁。又插入意向锁与gap lock和 Next-key lock冲突,即一个事务想要获取插入意向锁,如果有其他事务已经加了gap lock或 Next-key lock,则会阻塞。
  3. 场景中两个事务都持有gap lock,然后又申请插入意向锁,此时都被阻塞,循环等待造成死锁。

5

https://www.cnblogs.com/paul8339/p/6877729.html

Intention shared (IS): Transaction T intends to set S locks on individual rows in table t.
Intention exclusive (IX): Transaction T intends to set X locks on those rows.

Before a transaction can acquire an S lock on a row in table t, it must first acquire an IS or stronger lock on t. Before a transaction can acquire an X lock on a row, it must first acquire an IX lock on t.

The main purpose of IX and IS locks is to show that someone is locking a row, or going to lock a row in the table.

处理行锁和表锁之间的冲突,用于表明“某个事务正在某一行上持有了锁,或者准备去持有锁”。

 XIXSIS
X 冲突 冲突 冲突 冲突
IX 冲突 兼容 冲突 兼容
S 冲突 冲突 兼容 兼容
IS 冲突 兼容 兼容 兼容

默认(rr)情况下,innodb使用next-key locks来锁定记录。
但当查询的索引含有唯一属性的时候,Next-Key Lock 会进行优化,将其降级为Record Lock,即仅锁住索引本身,不是范围。
 
An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6, respectively, each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.
 
注:插入意向锁并非意向锁,而是一种特殊的间隙锁。(只是名字叫“意向”?)
 GapInsert IntentionRecordNext-Key
Gap 兼容 兼容 兼容 兼容
Insert Intention 冲突 兼容 兼容 冲突
Record 兼容 兼容 冲突 冲突
Next-Key 兼容 兼容 冲突 冲突

表注:横向是已经持有的锁,纵向是正在请求的锁

本文提出了2个经典死锁案例:

1 duplicate primary key

2 GAP与Insert Intention

但本文解释不如人意

6

https://blog.csdn.net/and1kaney/article/details/51214001,该文试图解释第1个死锁duplicate primary key

我先把官方文档对于insert 加锁的描述贴出来

INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.Prior to inserting the row, a type of gap lock called an insertion intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap.If a duplicate-key error occurs, a shared lock on the duplicate index record is set. This use of a shared lock can result in deadlock should there be multiple sessions trying to insert the same row if another session already has an exclusive lock. 

大体的意思是:insert会对插入成功的行加上排它锁,这个排它锁是个记录锁,而非next-key锁(当然更不是gap锁了),不会阻止其他并发的事务往这条记录之前插入记录。在插入之前,会先在插入记录所在的间隙加上一个插入意向gap锁(简称I锁吧),并发的事务可以对同一个gap加I锁。如果insert 的事务出现了duplicate-key error ,事务会对duplicate index record加共享锁。这个共享锁在并发的情况下是会产生死锁的,比如有两个并发的insert都对要对同一条记录加共享锁,而此时这条记录又被其他事务加上了排它锁,排它锁的事务提交或者回滚后,两个并发的insert操作是会发生死锁的。

 

几个例子

1)select for update未命中唯一索引 gap锁阻塞插入意向锁

2)插入意向锁相互不阻塞

3) insert 行记录排他锁阻塞select in share mode

4)事务2(S GAP)<—事务3(S GAP)<—事务2(插入意向锁)<–事务3(插入意向锁)   事务3,事务2,事务3形成死锁。(不懂)

 7

https://blog.csdn.net/wind_sunshine/article/details/84275411【重点】,该文讲清楚了GAP与Insert Intention的死锁

这里我们清楚的知道 --“注意 T1 的Gap,Insert intention ,T2 的Gap 都是锁的同一个地方 “space id 0 page no 198 n bits 80””—3个锁锁住同一个地方的原因了。因为customer_id = 3 和customer_id =5 都是属于同一个gap(2,6)。

T1 持有 gap (2,6) X锁,同时有个 insert intention (2,6)的X锁在等待gap(2,6)的X锁的释放;

T2 持有 gap(2,6) X锁。

这就是导致T1的insert 语句执行不下去的真正原因。 当T2的insert 语句执行的时候,(即F语句)可以预见,T2也会有个 insert intention(2,6)的X锁在等待gap(2,6)的X锁的释放。这样就形成了死锁。

         分析到这里就结束了么?好像那个地方有点不对。T1本身不就是拥有了一个gap(2,6)的X锁么?等等,为什么在T1拥有gap(2,6)X锁的情况下,T2还可以拥有gap(2,6)X锁?X锁同X锁不是不兼容的么(看看兼容矩阵)?

IX与IX兼容,X与X不兼容。T1和T2 同时拥有对于表order的IX锁是可以理解的;但是T1和T2 同时拥有对于表order的index customer_id的X锁似乎就无法理解了。

唯一错的地方是官方文档上面没有介绍除了这个(IS,IX,S,X)的兼容矩阵外,在Mysql实现内部还有一个更加精确的被称为“precise mode”的兼容矩阵。(该矩阵没有出现在官方文档上,是有人通过Mysql lock0lock.c:lock_rec_has_to_wait源代码推测出来的。)

         G    I     R    N (已经存在的锁,包括等待的锁)
  G   +     +    +     + 
  I    -      +    +     -
  R   +     +     -     -
  N   +     +     -     -
  + 代表兼容, -代表不兼容. I代表插入意图锁,
  G代表Gap锁,I代表插入意图锁,R代表记录锁,N代表Next-Key锁.

(http://www.mysqlops.com/2012/05/19/locks_in_innodb.html#more-3169)

这里需要注意的一点是,存在Insert Intention 锁时,申请Gap锁是允许的;但是存在Gap锁时,申请Insert Intention锁时是被阻止的。

8 https://www.cnblogs.com/jay-huaxiao/p/11456921.html

insert语句会对插入的这条记录加排他记录锁,在加记录锁之前还会加一种 GAP 锁,叫做插入意向(insert intention)锁,如果出现唯一键冲突,还会加一个共享记录(S)锁。 

https://www.aneasystone.com/archives/2017/12/solving-dead-locks-three.html

插入:意向排他锁,插入意向锁,如果该间隙已被加上了 GAP 锁或 Next-Key 锁,则加锁失败进入等待;

INSERT 加锁流程如下(参考):

S锁记录锁

这里的表述其实并不准确,有兴趣的同学可以去阅读 InnoDb 的源码分析 INSERT 语句具体的加锁过程,我在 《读 MySQL 源码再看 INSERT 加锁流程》 这篇博客中有详细的介绍。

10 https://blog.csdn.net/wanghao112956/article/details/91957538【重点】,该文圆了duplicate primary key的死锁

 

插入意向锁居然是X锁,S阻塞X,没毛病

结论:2个经典死锁案例飙蓝加粗黄色底

gap锁X和S同样效果,能被n个事务共同进入

gap锁相互不排斥,但是排斥插入意向锁

insert时,其加锁过程为先在插入间隙上获取插入意向锁(可能之前还有个意向排锁),插入数据后再获取插入行上的排它record锁。

意向锁:处理行锁和表锁之间的冲突,但插入意向锁并非意向锁,而是一种特殊的间隙锁。

插入意向锁互不阻塞,能共享,但不代表它是S锁;像gap X锁,就能相互共享

record锁一般是X锁,除了一种情况,duplicate primary key时会加recode S锁

S 记录锁排斥插入意向X锁

附加:

1 线上报大量的deadlock 通过命令登录mysql 通过此SHOW ENGINE INNODB STATUSG将打印最近一次死锁

2 业务层死锁

一种mysql jvm死锁

原文地址:https://www.cnblogs.com/silyvin/p/12685824.html