前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >死锁案例之八

死锁案例之八

作者头像
用户1278550
发布2018-08-09 14:05:57
3590
发布2018-08-09 14:05:57
举报
文章被收录于专栏:idba

一 前言

死锁其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发朋友都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。

二 案例分析

2.1 业务场景

业务上的主要逻辑:

首先执行插入数据,如果插入成功,则提交。如果插入的时候报唯一键冲突,则执行更新。 如果同时出现三个并发在执行数据初始化动作,sess1 插入成功,sess2 和 sess3插入遇到唯一键冲突,插入失败,则都执行执行更新,于是出现死锁。

2.2 环境准备

MySQL 5.6.24 事务隔离级别为RR

代码语言:javascript
复制
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,1);

T2

insert into ty (c1,c2,c3) values(4,4,2);

T3

insert into ty (c1,c2,c3) values(4,4,3);

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 死锁日志
代码语言:javascript
复制
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相互竞争,在缺乏业务逻辑场景的情况下,因为不知道还有insert操作,很难得到分析死锁的有效思路。

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其实比较复杂,需要多动手实践,理清加锁流程。

推荐阅读

如何阅读死锁日志

漫谈死锁

死锁案例之一

死锁案例之二

死锁案例之三

死锁案例之四

死锁案例之五

死锁案例之六

死锁案例之七

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-03-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一 前言
  • 二 案例分析
    • 2.1 业务场景
      • 2.2 环境准备
        • 2.3 测试用例
          • 2.4 死锁日志
            • 2.5 分析死锁日志
              • 2.6 解决方法
              • 三 小结
              • 推荐阅读
              相关产品与服务
              云数据库 MySQL
              腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档