乐观锁失败异常ObjectOptimisticLockingFailureException

一、问题描述

新开发的系统,往往可能需要将旧版的系统中的历史数据,用脚本的方式在新系统中跑一遍业务流程,其实可能是用Java代码自动调用一些业务流程接口。
在执行过程中发现报错:

2021-01-27 19:32:46.300 [http-nio-5090-exec-4] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet]:182 - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception 
[Request processing failed; 
nested exception is org.springframework.orm.ObjectOptimisticLockingFailureException: 
Object of class [com.etc.trade.domain.ProcessBill] with identifier [378]: 
optimistic locking failed; 
nested exception is org.hibernate.StaleObjectStateException: 
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.etc.trade.domain.ProcessBill#378]] with root cause

报错显示,id=378的ProcessBill对象的乐观锁失败,该记录被另外一个事务修改或删除。

二、排查定位

这种异常一般是由于,数据库里的某一条记录的某一个版本对应的信息,同时被两个事务读取到并试图进行写操作(包括更新和删除);这种情况下第一个写成功的事务不会有影响,第二个事务再对同一版本的同一记录进行写操作时,抛出乐观锁异常ObjectOptimisticLockingFailureException

2.1 乐观锁

同悲观锁一样,乐观锁一样也是为了一定程度上解决并发问题。
乐观锁的实现是CAS机制(compare and swap),但需注意ABA问题。
在数据库上,乐观锁的实现一般是采用类似的版本号机制,相比悲观锁,它开销小,效率高,适用于冲突不多的多读场景;即保持最乐观的态度,赌你不会遇到多读少写下很小几率的并发问题。
如果运气不好遇到了并发问题,乐观锁的处理是只会抛出一个这样的异常,进一步处理的权力交由程序员。
乐观锁版本号机制示例如下:

UPDATE `process_bill` 
SET `status` = 'new status',
    `version` = `version` + 1 
WHERE
	`bill_no` = 'B2021013016266789' AND `version` = 2

2.2 分析推测

  • 条件: 本项目中使用了JPA操作数据库,基于@Version的乐观锁机制,
    对每一个流程单processBill,会有多次不同接口调用(即多次业务流程推进),上一个接口A返回时会触发一个异步任务T,在拿到这个接口A的返回后,脚本会触发调用下一个接口B。
  • 结果
    此时异步任务T和下一个接口B可能取到了同一个版本的processBill,而异步任务T先将version由2变为3,之后下一个接口B再试图将version由2变为3时就必然失败。

三、问题处理

乐观锁异常有多种解决方案,根据不同项目的实际业务场景采取各自适合的方式。
对本文中的场景,由于异步任务T实际上不是耗时任务,所以最终将异步任务T改为在接口A中同步处理。
另外,常见的处理方式有:

  1. 对此异常添加重试机制
  2. @Query原生写操作,避免JPA的@Version乐观锁写操作的机制
  3. 写操作使用MySQL行锁的悲观锁(for update),在数据库层面最大程度避免了并发问题,但数据库锁时间长,开销大,更应该在应用层做处理
  4. 必要时在应用层对关键对象,使用zookeeper等分布式锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DayDayUp丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值