线上生产问题系列之 - 锁和事务(@transactional)引发的问题

本文探讨了一种业务场景下,采用加锁串行化执行更新账户操作时遇到的问题。由于锁和事务提交的不同步导致锁未能生效,进而引发乐观锁更新异常。文中详细分析了问题原因并给出了解决方案。

#现象描述
运营突然找到开发人员反馈:交易成功,但是账户数据不对。开发查看日志发现了大量的乐观锁更新异常。
#背景描述
简略的业务背景是这样,一个交易请求过来,需要两个主要操作,一个是记录交易记录,另一个是更新账户数据。这里我们默认为这两个操作是同步的,也就是需要在一个请求中串联执行完成后返回。
并发更新账户,有两种方式:

  • 使用乐观锁加上重试机制,也就是数据库字段加一个字段(比如:version)更新是对比version是否是同一个,如果不是则抛异常。配合重试机制再次执行更新操作达到最终更新完成的目标。
  • 加锁串行化执行。

两种方案各有优缺点。本文讨论的是第二种方案。
我们的业务系统实现时采用了第二种方案,但是保留了乐观锁只是没有重试机制,才有了现象描述中的乐观锁更新异常。
方案的简略代码是这样的:

  @Transactional
    public void transfer(......) {
      lock.lock();

       .....do something
       accountRepository.save(accountInfo)

      lock.unlock();
    }

这里的lock可以是分布式锁也可以是synchronized及其他,这里只是个实例。这个代码的意思是在更新账户的时候加锁,串行执行。如果锁生效那么就不会出现乐观锁异常,那问什么锁会失效呢。
#问题分析
回到方案最初的目的,加锁的目的在于保证更新账户操作是串行的。那么在lock和unlock之间的save如果成功就达到了目的。问题就出在了这里,这里的save,commit了吗?我们看这个方法是使用了@Transactional ,事务交给了Spring 管理。Spring的具体实现方式呢就是通过AOP。AOP简单点讲就是在你方法执行的前后去做统一的一个处理。那么问题就来了,这个lock和unlock操作是在方法里面执行的,但是事务的提交是在unlock后切面里面执行的。这样相当于没有生效。

#解决方案
解决方案就很简单了,锁加在方法之外就可以了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值