不溜過客 2025-06-22 10:50 采纳率: 98.2%
浏览 142
已采纳

Spring Boot 3.4中如何解决“Row was updated or deleted by another transaction”异常?

在Spring Boot 3.4中,如何有效解决“Row was updated or deleted by another transaction”异常? 此问题通常源于乐观锁机制或并发事务冲突。当多个事务同时修改同一数据库记录时,可能触发该异常。为解决此问题,首先可通过引入版本字段(如`@Version`注解)实现乐观锁控制,确保更新操作基于最新版本数据。其次,调整隔离级别(如使用`@Transactional(isolation = Isolation.SERIALIZABLE)`)可减少并发冲突,但需权衡性能影响。此外,重试机制(如结合Spring Retry)能在异常发生时自动重试事务,提升系统鲁棒性。最后,优化业务逻辑,尽量缩短事务持有时间,降低冲突概率。 例如,在代码层面添加自定义注解或AOP切面捕获异常并处理重试逻辑,是常见解决方案之一。
  • 写回答

1条回答 默认 最新

  • 诗语情柔 2025-06-22 10:50
    关注

    1. 问题概述与背景分析

    在Spring Boot 3.4中,“Row was updated or deleted by another transaction”异常通常由乐观锁机制或并发事务冲突引发。当多个事务同时修改同一数据库记录时,可能导致数据不一致,从而触发该异常。以下将从常见技术问题、分析过程和解决方案等角度展开讨论。

    • 异常原因:乐观锁(如`@Version`注解)检测到版本号不匹配。
    • 并发场景:多个事务试图在同一时间更新同一记录。
    • 影响范围:可能导致业务逻辑失败或数据丢失。

    2. 解决方案之一:引入乐观锁控制

    通过`@Version`注解实现乐观锁是解决该问题的常见方法之一。此方法确保每次更新操作都基于最新版本的数据。

    @Entity
    public class ExampleEntity {
        @Id
        private Long id;
    
        @Version
        private Integer version;
    }
    

    在实际应用中,可以通过捕获`OptimisticLockException`来处理版本冲突:

    try {
        entityManager.merge(entity);
    } catch (OptimisticLockException e) {
        // 处理冲突逻辑
    }
    

    3. 解决方案之二:调整隔离级别

    通过设置事务的隔离级别为`SERIALIZABLE`,可以减少并发冲突的发生概率,但需权衡性能影响。

    隔离级别描述
    READ_UNCOMMITTED允许脏读,性能最高但安全性最低。
    READ_COMMITTED防止脏读,但可能产生不可重复读。
    REPEATABLE_READ防止不可重复读,但可能产生幻读。
    SERIALIZABLE完全隔离,防止所有并发问题,但性能较低。

    代码示例:

    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void updateEntity(ExampleEntity entity) {
        // 更新逻辑
    }
    

    4. 解决方案之三:实现重试机制

    结合Spring Retry库,可以在异常发生时自动重试事务,提升系统鲁棒性。

    @Retryable(value = OptimisticLockException.class, maxAttempts = 3, backoff = @Backoff(delay = 500))
    public void retryableUpdate(ExampleEntity entity) {
        entityManager.merge(entity);
    }
    

    流程图展示重试机制的工作原理:

    mermaid
    graph TD;
        A[开始事务] --> B[尝试更新];
        B --> C{是否成功};
        C --是--> D[提交事务];
        C --否--> E{重试次数达到上限?};
        E --是--> F[回滚并抛出异常];
        E --否--> G[等待延迟];
        G --> H[重新开始事务];
    

    5. 解决方案之四:优化业务逻辑

    尽量缩短事务持有时间,降低冲突概率。例如,将大事务拆分为多个小事务,或者优化查询逻辑以减少锁定资源的时间。

    自定义注解或AOP切面捕获异常并处理重试逻辑:

    @Aspect
    @Component
    public class TransactionRetryAspect {
    
        @Around("@annotation(retryable)")
        public Object handleRetry(ProceedingJoinPoint joinPoint, Retryable retryable) throws Throwable {
            int maxAttempts = retryable.maxAttempts();
            int delay = retryable.backoff().delay();
            for (int i = 0; i < maxAttempts; i++) {
                try {
                    return joinPoint.proceed();
                } catch (OptimisticLockException e) {
                    if (i == maxAttempts - 1) {
                        throw e;
                    }
                    Thread.sleep(delay);
                }
            }
            return null;
        }
    }
    

    6. 综合考虑与实践建议

    针对“Row was updated or deleted by another transaction”异常,应根据具体业务场景选择合适的解决方案。例如,对于高频并发写入场景,优先考虑乐观锁与重试机制;对于低频但对一致性要求极高的场景,可适当调整隔离级别。

    此外,结合实际需求评估性能影响,并通过监控工具(如Spring Actuator)持续优化系统表现。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 10月23日
  • 创建了问题 6月22日