瞎回答一个粉丝的死锁问题!

Posted 孤独烟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了瞎回答一个粉丝的死锁问题!相关的知识,希望对你有一定的参考价值。

引言

刚起床,本来想去看看电视,结果手抖看到了某个粉丝的留言,于是有了此文。本问题出自某粉丝给我的留言,如下图所示

为啥说是瞎回答呢?
  • 没贴死锁日志

  • 没具体过程

所以,我就靠经验来猜测一下!

正文

先说一个东西

insert into A select * from  B;

这个语句大家都知道是什么意思了,但是你不要以为只有A表加锁,B表也是加锁的。而且加锁方法还是有讲究的。
老规矩假设B表结构如下,pId为主键,name上无索引

瞎回答一个粉丝的死锁问题!

按主键插入

把该粉丝的SQL简化一下,变成

insert A表 select * from B表 where 主键 in (1,2,7)

这里虽然是in语句,但是字段为主键。因此,此时索引是会生效的,而且是会逐步锁pId =1,2,7的聚簇索引。也就是说,你可以这么理解,mysql按如下顺序执行的

insert A表 select * from B表 where 主键 in (1);
insert A表 select * from B表 where 主键 in (2);
insert A表 select * from B表 where 主键 in (7);

因此是逐步锁pId =1,2,7的聚簇索引。
说一个特殊情况,如果你执行的是

insert A表 select * from B表 where 主键 in (8)

注意了pId = 8 这一列,在B表是不存在。那么此时存在间隙锁,锁住的是(7,11)这个间隙。
考虑到在这篇文章里要想将insert的加锁机制讲清楚篇幅太小,改天有时间细说。这里大家先这么记

insert into B values (13,55);

会在索引为pId=13位置加上锁,并在(11,+∞)的位置加上插入意向锁。插入意向锁、间隙锁和行锁的兼容性如下

瞎回答一个粉丝的死锁问题!

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

好了。。现在可以重现死锁的场景了。如下图所示

如图所示
(1) Session 1 在(11,+∞)的位置加上插入意向锁,并获得pId=13记录锁。
(2) Session 2 意图获得(13,+∞)的间隙锁,根据上方的兼容性表。此时存在插入意向锁,因此间隙锁可以获得。
(3) Session 1 意图获得(14,+∞)的插入意向锁,根据上方的兼容性表。此时存在间隙锁,因此无法获得插入意向锁,等待Session2释放锁
(4) Session 2 又想获得得pId=13记录锁,因此等待Session 1 释放锁。
于是,死锁形成!

注意了,因为这里都是我的猜测。我假设执行的SQL是

insert into B1 select * from B where pId in (2013);

也就是说pId in (20,13)里头的元素是无规律的。为什么呢,如果Session 2执行的SQL是pId in (20,30,32,35,36...)这样递增的,永远无法请求Session 1所获得的锁。所以,我后台询问了一下他


证实了我的猜测!

如何解决

so easy!把隔离级别改成RC就行!RR隔离级别下insert A select B where B.COL=**,innodb层会对B表满足条件的数据进行加锁,但是RC模式下B表记录不会加任何innodb层的锁

总结

嗯,希望我说的该粉丝能懂吧!



以上是关于瞎回答一个粉丝的死锁问题!的主要内容,如果未能解决你的问题,请参考以下文章

sqlserver-处理死锁

说马斯克不懂技术瞎比比,工程师被在线开除!

说马斯克不懂技术瞎比比,工程师被在线开除!

使用级联约束的可重现死锁

求你别自己瞎写工具类了,Spring自带的这些他不香麽?

Java并发编程实战 04死锁了怎么办?