搜公众号
推荐 原创 视频 Java开发 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库
Lambda在线 > 孤独烟 > 瞎回答一个粉丝的死锁问题!

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

孤独烟 2019-02-15
举报

引言

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

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

  • 没具体过程

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

正文

先说一个东西

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层的锁

总结

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



版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《瞎回答一个粉丝的死锁问题!》的版权归原作者「孤独烟」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

举报