You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决未显式加锁却频繁抛出PessimisticLockingFailureException的问题

PessimisticLockingFailureException异常分析与解决方案

异常背景

相关异常堆栈如下:

org.springframework.dao.PessimisticLockingFailureException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.PessimisticLockException: could not execute statement
    at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:240) ~[spring-orm-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.orm.hibernate5.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:804) ~[spring-orm-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:639) ~[spring-orm-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:633) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:386) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE]
    at com.sun.proxy.$Proxy168.delete(Unknown Source) ~[?:?]

对应的业务代码如下:

class Suppress {
Job job = serviceImpl.getJob(jobId);
String name = job.getJobName();
//do something with name
serviceImpl.delete(job)
}

@Transactional(propagation = Propagation.REQUIRED, readOnly = true, rollbackFor = Throwable.class)
class ServiceImpl{
getJob(int id){
    daoImpl.get(id)
}

delete(Job job){
   daoImpl.delete(job)
}
}

根因分析

  • 事务配置错误:ServiceImpl类上标注了readOnly = true的全局事务配置,delete方法未单独指定事务属性,继承了只读配置。删除属于写操作,运行在只读事务中时,数据库无法分配写锁,和其他持有该行读锁的事务产生冲突触发异常。
  • 事务边界拆分不合理:Suppress类中先后调用getJob、delete两个独立事务方法,第一个只读事务加载Job对象后会提交并释放读锁,第二个删除事务需要重新申请写锁,两个事务的间隙内如果有其他事务修改、锁定了对应行记录,就会触发锁冲突。
  • 实体状态异常:第一个事务加载的Job对象在事务提交后变为游离态,delete方法执行时需要先将游离态实体重新关联到持久化上下文,关联过程中如果遇到行锁占用、版本冲突也会触发锁异常。

解决方案

  • 修正事务属性配置:给delete方法单独添加事务注解,覆盖类级别的只读配置,保证写操作运行在可写事务中:
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = Throwable.class)
    delete(Job job){
       daoImpl.delete(job)
    }
    
  • 合并事务边界:将查询、操作、删除的逻辑封装到同一个事务中执行,避免两个独立事务之间的锁间隙被其他请求抢占。可以在ServiceImpl中新增组合方法,或者在外层调用逻辑上加事务注解,保证整个操作链路在同一个事务内完成。
  • 显式配置锁策略:如果存在较高并发修改同一条Job记录的场景,可以在查询阶段就显式申请悲观写锁,避免后续执行写操作时出现锁冲突:
    getJob(int id){
        daoImpl.get(id, LockModeType.PESSIMISTIC_WRITE)
    }
    
  • 新增重试机制:针对偶发的锁冲突,可以给删除逻辑添加3-5次的有限重试,捕获PessimisticLockingFailureException后间隔100-300ms再重试,提升并发场景下的操作成功率。

内容的提问来源于stack exchange,提问作者Hemanth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 10:39:01