如果两个线程确实读取和写入一个相同的文档:
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如何处理事务冲突?或者,如果它使用数据库级别的锁,它如何处理读写冲突?
发布于 2018-11-09 05:12:00
我在MongoDB手册中找到了一些参考资料,解决了我自己的问题。
MongoDB使用什么类型的锁定? MongoDB使用多粒度锁定1,允许操作在全局、数据库或集合级别锁定,并允许单个存储引擎在集合级别以下实现自己的并发控制(例如,在WiredTiger中的文档级别)。
MongoDB使用从集合、数据库到全局的多个级别锁定。然而,尽管它支持多个级别的锁定,但您可以访问的唯一级别是集合级别,意味着您不能在事务中创建或删除数据库或集合。还意味着获取一个要锁定在集合中的文档将导致整个集合被锁定。
受限操作 在多文档事务中不允许执行下列操作:
为了解决冲突,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;
}
}
}参见手册中的更多语言版本;)!
参考文献
https://stackoverflow.com/questions/53202345
复制相似问题