mongodb - MongoDB 如何处理事务冲突?
问题描述
如果两个线程都读写同一个文档:
try (ClientSession clientSession = client.startSession()) {
clientSession.startTransaction();
result = collection.find(clientSession, keyOfDoc);
if (result blah blah blah) {
// Change the doc
collection.insertOne(clientSession, doc);
}
clientSession.commitTransaction();
}
从事务的目的来看,一个线程应该得到另一个线程的编辑版本。
但是,当两个线程都开始事务时,它们都获得了读锁,然后读取了文档。两个线程都获得了旧版本的文档。当他们需要写文档时,他们会尝试获取写锁,这会使事务不是原子的。
另一种情况是写-写冲突。
try (ClientSession clientSession = client.startSession()) {
clientSession.startTransaction();
collection.insertOne(clientSession, docDifferent);
collection.insertOne(clientSession, docSame);
clientSession.commitTransaction();
}
两个线程都先获取不同文档的写锁,然后再获取同一个文档的写锁,这是另一个事务冲突。
MongoDB 使用什么级别的锁?我知道他们在 2.2 版本之前使用实例级别,而从 4.0 开始支持事务。如果MongoDB不使用数据库级锁,MongoDB如何处理事务冲突?或者如果它使用数据库级别的锁,它是如何处理读写冲突的?
解决方案
我在 MongoDB 手册中找到了一些解决我自己问题的参考资料。
MongoDB 使用什么类型的锁定?
MongoDB 使用多粒度锁定1,允许操作在全局、数据库或集合级别锁定,并允许各个存储引擎在集合级别以下(例如,在 WiredTiger 中的文档级别)实现自己的并发控制。
MongoDB 使用从集合、数据库到全局的多级锁定。但是,尽管它支持多级锁定,但您可以访问的唯一级别是集合级别,这意味着您不能在事务中创建或删除数据库或集合。这也意味着获取一个要锁定在集合中的文档将导致整个集合被锁定。
受限操作
多单据交易中不允许进行以下操作:
影响数据库目录的操作,例如创建或删除集合或索引。例如,多文档事务不能包含会导致创建新集合的插入操作。
listCollections 和 listIndexes 命令及其辅助方法也被排除在外。
非 CRUD 和非信息性操作,例如 createUser、getParameter、count 等及其助手。
为了解决冲突,MongoDB会在发生冲突时向无法获得锁的访问者发送错误消息。
重试事务
无论 retryWrites 是否设置为 ,事务中的各个写入操作都不可重试
true
。如果操作遇到错误,返回的错误可能有一个 errorLabels 数组字段。如果错误是暂时性错误,errorLabels 数组字段包含“TransientTransactionError”作为元素,并且可以重试整个事务。
意思是当访问者收到MongoException
异常.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)
时,访问者应该关闭会话,并重做事务。访问者应该重做并重新提交,直到提交成功。
您可以简单地使用此方法(从手动示例修改):
public static <T> T transactWithRetry(Callable<T> transactional) throws Exception {
while (true) {
try {
return transactional.call();
} catch (MongoException ex) {
if (!ex.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)) throw ex;
}
}
}
请参阅手册中更多语言的版本;)!
参考
推荐阅读
- reporting-services - SSRS 通过 URL 调用页脚中的子报表
- reactjs - 有没有更好的方法来编写此检查以查看反应中的道具上是否存在值?
- hamcrest - Hamcrest 定位器浓缩咖啡
- ios - 对齐文本与填充SwiftUI
- webpack - Next.js Sentry Sourcemap 大小过大
- python-3.x - tox 不运行狮身人面像
- django - 在 Django 中显示用户对帖子的评论
- ios - 我试图在不使用情节提要的情况下使用 swift (Xcode) 将标题部分添加到我的个人资料页面
- ado.net - SAP HanaDB SQL 中声明的数组/表变量值错误
- c++ - 无法理解如何摆脱 goto