MongoDB如何处理交易冲突?

时间:2018-11-08 06:11:50

标签: mongodb concurrency locking

如果两个线程都读取和写入同一文档:

try (ClientSession clientSession = client.startSession()) {
    clientSession.startTransaction();
    collection.insertOne(clientSession, docDifferent);
    collection.insertOne(clientSession, docSame);
    clientSession.commitTransaction();
}

从事务的目的出发,一个线程应获取另一个线程的编辑版本。

但是,当两个线程都开始事务时,它们都获得了读取锁,然后读取了文档。这两个线程都获得了旧版本的文档。当他们需要编写文档时,他们尝试获取写锁,这将使事务不是原子的。

另一种情况是写冲突。

{{1}}

两个线程首先获取不同文档的写锁,然后又获取同一文档的写锁,因为这是另一个事务冲突。

MongoDB使用什么级别的锁?我知道他们使用2.2之前的实例级别,而4.0以后才支持事务。如果MongoDB不使用数据库级锁,MongoDB如何处理事务冲突?或者,如果它使用数据库级别的锁,它将如何处理读写冲突?

1 个答案:

答案 0 :(得分:0)

我在《 MongoDB手册》中找到了一些参考资料,这些参考资料解决了我自己的问题。

  

MongoDB使用哪种类型的锁定?

     

MongoDB使用多粒度锁定1,它允许操作锁定在全局,数据库或集合级别,并允许各个存储引擎在集合级别(例如,在文档级别)以下实现自己的并发控制级别)。

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;
        }
    }
}

manual中查看更多语言的版本;)!


参考

Transactions — MongoDB Manual

FAQ: Concurrency — MongoDB Manual