一 前言
死锁其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发朋友都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。
二 案例分析
2.1 业务场景
业务上的主要逻辑:
首先执行插入数据,如果插入成功,则提交。如果插入的时候报唯一键冲突,则执行更新。 如果同时出现三个并发在执行数据初始化动作,sess1 插入成功,sess2 和 sess3插入遇到唯一键冲突,插入失败,则都执行执行更新,于是出现死锁。
2.2 环境准备
MySQL 5.6.24 事务隔离级别为RR
create table ty (
id int not null primary key auto_increment ,
c1 int not null default 0,
c2 int not null default 0,
c3 int not null default 0,
unique key uc1(c1),
unique key uc2(c2)
) engine=innodb ;
insert into ty(c1,c2,c3) values(1,3,4),(6,6,10),(9,9,14);
2.3 测试用例
为了方便分析死锁日志,三个会话插入的c3的值分别为1 2 3 ,生产上其实是相同的值。
|
sess1 |
sess2 |
sess3 |
|
begin; |
begin; |
begin; |
T1 |
insert into ty (c1,c2,c3) values(4,4,4); |
|
|
T2 |
|
insert into ty (c1,c2,c3) values(4,4,4); |
|
T3 |
|
|
insert into ty (c1,c2,c3) values(4,4,4); |
T4 |
commit
|
|
|
T5 |
|
update ty set c3=5 where c1=4; |
|
T6 |
|
|
update ty set c3=5 where c1=4; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction |
2.4 死锁日志
2018-03-28 10:04:52 0x7f75bf2d9700
*** (1) TRANSACTION:
TRANSACTION 1870, ACTIVE 76 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 399265, OS thread handle 12, query id 9 root updating
update ty set c3=5 where c1=4
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 28 page no 4 n bits 72 index uc1 of table
`test`.`ty` trx id 1870 lock_mode X locks rec but not gap waiting
*** (2) TRANSACTION:
TRANSACTION 1871, ACTIVE 32 sec starting index read,
thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 399937, OS thread handle 16, query id 3 root updating
update ty set c3=5 where c1=4
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 28 page no 4 n bits 72 index uc1 of table
`test`.`ty` trx id 1871 lock mode S
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 28 page no 4 n bits 72 index uc1 of table
`test`.`ty` trx id 1871 lock_mode X locks rec but not gap waiting
*** WE ROLL BACK TRANSACTION (2)
其实单单从日志上查看只看到两个事务的update相互竞争,在缺乏业务逻辑场景的情况下,很难得到有效思路。
2.5 分析死锁日志
T1 s1 执行insert操作,检查唯一性且插入成功,持有c1=4记录行的行锁。
T2 s2 insert遇到唯一键冲突,申请加锁Lock S Next-key Lock 日志显示为index uc1 of table test.ty trx id 1870 lock mode S waiting
T3 与s2相同,s3 insert遇到唯一键冲突,申请加锁Lock S Next-key Lock 日志显示为index uc1 of table test.ty trx id 1870 lock mode S waiting
T4 sess1 执行commit操作, 此时sess2 和sess3 同时获取Lock S Next-key Lock。
T5 应用收到唯一键冲突,sess2执行update 操作需要申请c=4的行锁,与sess3的持有的Lock S Next-key Lock不兼容,等待sess3释放Lock S Next-key Lock。
T6 与sess2 类似 sess3执行update 操作需要申请c=4的行锁,与sess2的持有的Lock S Next-key Lock不兼容,等待sess2释放Lock S Next-key Lock。出现循环等待,发生死锁。
2.6 解决方法
本案例的解决方式其实和前文 死锁案例之七 一致,使用insert on duplicate key。案例七与本文导致死锁业务逻辑极为相似,为什么呢?因为都是同一组开发哥哥写的。
三 小结
导致死锁的根本原因是不同事务申请锁的顺序不一样出现循环等待,开发同学在设计高并发的业务场景时,需要着重思考这一点,并且尽量规避业务场景设计不合理导致死锁。
另外就是insert 的加锁机制相对update其实比较复杂,需要多动手实践,理清加锁流程。
推荐阅读
如何阅读死锁日志
漫谈死锁
死锁案例之一
死锁案例之二
死锁案例之三
死锁案例之四
死锁案例之五
死锁案例之六
死锁案例之七
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1148
183.71 KB下载数642
644.84 KB下载数2756
相关文章
发现更多好内容- 在 Java 中,charsequence 的具体用法究竟是什么?(java中charsequence的用法是什么)
- 如何利用 Java Quarter 进行数据挖掘?(java quarter如何进行数据挖掘)
- Java 中常见的比较操作符有哪些?(Java比较操作符有哪些)
- 在 Java 中,Integer 究竟有哪些作用呢?(java中integer的作用是什么)
- Java 实体类如何进行赋值操作?(java实体类怎么赋值)
- Java 中 switch case 的具体用法有哪些?(switchcase在java中的用法)
- 如何在 Java EMqtt 中实现消息确认机制?(Java EMqtt怎样实现消息确认机制)
- Java 性能调优的有效手段有哪些?(Java性能调优有何有效手段)
- 探讨Uncomtrade数据库的安全防护措施
- Java 如何获取某个接口的实现类?(Java怎么获取某个接口的实现类)