首页 > 解决方案 > 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如何处理事务冲突?或者如果它使用数据库级别的锁,它是如何处理读写冲突的?

标签: mongodbconcurrencylocking

解决方案


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

请参阅手册中更多语言的版本;)!


参考

事务 — MongoDB 手册

常见问题解答:并发 — MongoDB 手册


推荐阅读