看三年的CRUD程序员如何解决数据库死锁的

497 篇文章 2 订阅
495 篇文章 0 订阅
本文讲述了作者在工作中遇到的数据库死锁问题及其解决过程。通过分析错误信息,发现是由两个接口间的事务相互等待造成的。文章提出了三种解决方案:1. 使用多线程,虽然能解决死锁,但可能无法获取RPC响应;2. 事务细化处理,解决了响应问题,但不能保证所有情况下的数据一致性;3. 顺序处理,即先RPC调用再更新,是最终采用的方案,能有效避免交叉更新引发的死锁。最后,作者欢迎读者分享更多解决方案。
摘要由CSDN通过智能技术生成

问题出现

数据库死锁的问题不知道大家有没有见过,反正我干了三年多CRUD是第一次见。

接下来就一起来看看我遇到的这个死锁到底是怎样造成的?怎么解决的?

首先贴一下报错信息

### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: 
Deadlock found when trying to get lock; 
try restarting transaction; SQL []; 
Deadlock found when trying to get lock; 
try restarting transaction; 
nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: 
Deadlock found when trying to get lock; 
复制代码

当时的业务逻辑大概是这样的,如下图

现在给他简化一下

  • 两个接口A和B,分别会开始事务A和B,一条数据行d
  • 首先接口A会开启事务A去更新数据行d,拿到这条数据行的锁
  • 通过RPC调用接口B,接口A等待接口B的响应返回才能进行事务的提交
  • 接口B开启事务B去更新数据行d,但是数据行d的锁被事务A持有,事务B只能等待事务A释放锁才能进行数据的更新

这就是典型的相互等待造成的死锁,一个持有锁,一直无法释放;一个等待锁,一直获取不到。

那么,这个问题是如何解决的呢

经过跟同事及leader的沟通,大概给出三种方案

多线程

针对上面造成死锁的问题,可以将RPC的调用另起一个线程去执行;主线程去执行数据更新的操作。

  • 首先接口A不需要等待接口B的返回就可以提交事务
  • 然后就算接口B执行比较快:A事务还没有提交的时候,B接口就已经执行到更新数据的地方,这时候会堵塞;但是没关系,无论如何A事务都会提交事务,无非是快慢的问题
  • 所以上述的死锁就会被解决

但是这种方式就是万无一失么,有什么问题呢?

  1. 多线程执行RPC调用,无法获取响应结果(当然多线程也能获取结果,但是获取结果的话,事务A还是无法提交造成死锁,没有意义)。
  2. 无法判断接口的成功与否,就丧失了事务失败回滚的意义
  3. 多线程能解决上述问题的死锁问题,但是稍微复杂一点的多表交叉更新还是会出问题,如下图

对事务进行细化处理

把接口A中的事务进行细化拆分:

  • 将需要更新数据库的逻辑拆分成一个方法,并在方法调用的时候开启事务
  • 当细分的方法执行成功后,再去进行RPC调用

这个解决了响应无法返回的问题,但还是那个问题,如果RPC调用失败了,更新的数据无法回滚。

顺序处理(也是最终的处理方案)

上述死锁问题的解决

  • 先RPC调用接口
  • 再进行更新操作

另外,顺序处理也是能解决复杂一点的多事务的多表更新死锁问题的。

首先我们得保证两个或多个开启事务的接口,对数据库数据的处理尽量避免交叉更新,应该按顺序更新。

比如对多线程处理的第三个问题解决,如图

其他方案

大伙们还有其他比较好的方案么,欢迎来沟通讨论啊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值