Java多线程、高并发秒杀时MySQL出现死锁原因(Deadlock found when trying to get lock)及对应解决方案

2021年4月19日 6点热度 0条评论 来源: 原来的1024

1. 死锁背景

1.1 在做高并发秒杀中创建订单、减库存步骤时出现异常:MySQLTransactionRollbackException: Deadlock found when trying to get lock,也就是出现了死锁

1.2 我们在学多线程时,知道死锁发生在两个线程互相去抢占对方的锁情况,如下图。

1.3  那我们这边出现死锁的原因是什么呢?出现死锁的原因有很多,比如相同记录行锁、索引锁以及GAP锁,我这边应该算是索引锁。在秒杀时,将创建订单、扣减商品库存放在了一个事务中,并且订单表中用到了商品表id作为外键,这个作为外键是关键,因为外键是一种索引,操作时会进行相应的加锁操作,导致发生死锁。

 

 

2. 死锁复现

2.1 使用MySQL客户端或命令行窗口,打开两个MySQL连接,我这边使用SQLyog,先使用 set @@autocommit=0; 关闭事务自动提交,并使用 select @@autocommit;  查看结果是否为0,为0则禁止成功,如下图。

2.2 我这边已经创建好了商品表以及订单表,你那边可以随便创建两个简单点的表,唯一要设置的就是其中一个表的主键id是另一个表的外键即可。

2.3 在MySQL连接A中插入一条订单数据,如下图。

2.4 接着在另一个MySQL连接B插入另一条订单数据,如下图。

2.5 接着返回MySQL连接A更新商品库存,如下图。

2.6 接着去MySQL连接B更新商品库存,如下图,也是等待并且最后等待超时。

2.7 先在两个连接都执行 rollback;回滚取消我们之前的操作,然后再执行2.3和2.4,接着在MySQL连接A执行库存更新语句,此时会处于等待状态,我们去另一个MySQL连接B执行库存更新语句,此时会有死锁提示,结果如下图。

 

3 死锁发生原因

我这边死锁原因大致是秒杀订单表中用到了商品表id作为外键导致索引锁,最终出现了死锁。

 

4. 死锁解决

4.1 取消外键索引

4.2 将订单创建和库存扣减顺序进行切换,先减库存,后创建订单

4.3 避免多线程操作,可以将多个操作放入队列,一个一个进行处理。

    原文作者:原来的1024
    原文地址: https://blog.csdn.net/yldmkx/article/details/115842714
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。