3

最近在项目尝试使用Mongodb的事务特性
遇到了一个情况,两个事务执行修改同一个数据的时候会返回下面的错误

org.springframework.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的事务锁处理方式是:
https://docs.mongodb.com/v4.4...

In-progress Transactions and Write Conflicts

If a transaction is in progress and a write outside the transaction modifies a document that an operation in the transaction later tries to modify, the transaction aborts because of a write conflict.
If a transaction is in progress and has taken a lock to modify a document, when a write outside the transaction tries to modify the same document, the write waits until the transaction ends.

正在进行的事务和写入冲突(谷歌翻译)

如果事务正在进行并且事务外的写入修改了事务中的操作稍后尝试修改的文档,则事务会因写入冲突而中止。
如果一个事务正在进行并且已经锁定修改文档,那么当事务外部的写操作试图修改同一个文档时,写操作会一直等到事务结束。

简单来讲 如果两个事务修改同一个文档就会报写冲突,不支持MySql那样的行锁-悲观锁。

原因

Mongodb的事务属于乐观事务,不同于MySql悲观事务
Mongodb的事务使用的隔离级别为SI(Snapshot Isolation,篇外连接)

1、乐观事务会有冲突检测,检测到冲突则立即throw Conflict(也就是上面提到的WriteConflict)
2、乐观事务推崇的是更少的资源锁定时间,达到更高的效率,跟以往我们使用的MySql事务还是有比较大的区别的
3、所以可以理解不支持MySql那样的行锁-悲观锁

MongoDb官方推荐在driver层重试,也就是出现WriteConflict异常自动进行重试。

总结

也就是不管使用Java、.Net、Nodejs、C++等等语言,出现冲突需要执行重试

事务API:https://docs.mongodb.com/manu...

官方新增了一个叫withTransaction的API方法,这个是可以自动重试事务,如果是直接使用mongo-driver是可以看一下有没有这个方法,有就使用这个就行。

对于使用SpringBoot框架 重试Mongodb事务可以看这里:https://segmentfault.com/a/11...


篇外-事务参数解析

  # 事务存活最长时间(默认60秒)
  transactionLifetimeLimitSeconds: 秒 
  # 事务锁超时最长时间(默认5毫秒)
  maxTransactionLockRequestTimeoutMillis: 毫秒
  # 同时执行读事务的最大个数(建议设置大一些)
  wiredTigerConcurrentReadTransactions: 个数
  # 同时执行写事务的最大个数(建议设置大一些)
  wiredTigerConcurrentWriteTransactions: 个数

修改(以参数maxTransactionLockRequestTimeoutMillis为例子)

方式一:使用这个可以在线修改这个值

db.adminCommand( { setParameter: 1, maxTransactionLockRequestTimeoutMillis: 36000000} );

方式二:启动的时候加入参数

mongod --setParameter maxTransactionLockRequestTimeoutMillis=36000000

方式三:在(/etc/mongod.cnf)中加入一下配置(老版配置文件)

setParameter = maxTransactionLockRequestTimeoutMillis=36000000

方式四:在新版yaml配置文件(/etc/mongod.cnf)中加入一下配置(推荐)

setParameter:
  # 事务锁超时最长时间(毫秒)
  maxTransactionLockRequestTimeoutMillis: 36000000

集群

如果是副本集分片建议在每台服务器都执行相同的配置


岁月安然
27 声望4 粉丝

随遇而安