参考原文: https://segmentfault.com/a/1190000040457995?sort=newest
问题:
使用Mongodb的事务:
pringframework.data.mongodb.UncategorizedMongoDbException: Command failed with error 112 (WriteConflict): 'WriteConflict' WriteConflict error: this operation conflicted with another operation. Please retry your operation or multi-document transaction.,
原因
Mongodb的事务属于乐观事务,不同于MySql悲观事务
Mongodb的事务使用的隔离级别为SI(Snapshot Isolation,篇外连接)
1、乐观事务会有冲突检测,检测到冲突则立即throw Conflict(也就是上面提到的WriteConflict)
2、乐观事务推崇的是更少的资源锁定时间,达到更高的效率,跟以往我们使用的MySql事务还是有比较大的区别的
3、所以可以理解不支持MySql那样的行锁-悲观锁
对于出现冲突后的处理方案
MongoDb官方推荐在driver层重试,也就是出现WriteConflict异常自动进行重试。
解决方案
经过查阅资料
spring-data-mongo官方推荐使用spring-retry框架进行重试
添加依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
启用spring-Retry
Application类上面需要添加@EnableRetry注解
方式一 简单使用:
propagation = Propagation.REQUIRED 也可以
@Retryable(value = UncategorizedMongoDbException.class, exceptionExpression = "#{message.contains('WriteConflict error')}", maxAttempts = 128, backoff = @Backoff(delay = 500)) @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class, timeout = 120) public void updateScoreRemark(String remark) { 业务代码 }
方式二 快捷使用
定义@MongoTransactional注解
import org.springframework.data.mongodb.UncategorizedMongoDbException; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.lang.annotation.*; /** * Mongo事务注解 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Retryable(value = UncategorizedMongoDbException.class, exceptionExpression = "#{message.contains('WriteConflict error')}", maxAttempts = 128, backoff = @Backoff(delay = 500)) @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class, timeout = 120) public @interface MongoTransactional { }
使用@MongoTransactional注解
@MongoTransactional public void updateScoreRemark(String remark) { 业务代码 }
注意事项
@Retryable
maxAttempts = 128 参数是最大重试次数,可自行调整
backoff = @Backoff(delay = 500) 重试间隔时间(毫秒) 可自行调整
@Transactional
propagation = Propagation.REQUIRES_NEW 是每次创建新的事务(传播级别)
propagation = Propagation.REQUIRED 也可以
由于mongodb出现WriteConflict 的时候会abort(rollback)事务,所以重试的时候需要生成新的事务
所以推荐在业务入口处增加【重试和事务】的注解,其他地方不要加,避免一个业务方法中出现两个事务。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
2022-07-29 分布式事务( XA) -- seata eurake springboot mysql (1.4.2)
2021-07-29 easyexcel 自动设置列宽(转)