1

我有一份需要阅读和更新的文件。同时,很可能另一个进程正在执行相同的操作,这会破坏文档更新。

例如,如果进程 A 读取文档 d 并向其添加字段 'a' 并写入文档,并且进程 B 在进程 A 写入之前读取文档 d,并添加字段 b 并写入文档,则无论哪个进程将更改写出会得到他们的改变,因为它破坏了第一个写的人的改变。

我已经阅读了这篇文章以及其他一些关于 mongo 的非常复杂的事务文章。有人可以描述一个简单的解决方案吗 - 我还没有遇到让我对此感到满意的东西。

https://www.mongodb.com/blog/post/how-to-select--for-update-inside-mongodb-transactions

[更新]-此外,我正在尝试扩充可能尚不存在的文档。如果文档不存在,我需要创建它。我还需要阅读它来分析它。一个关键是“relatedIds”(一个数组)。如果在其中找不到 id,我会推送到该数组。如果文档不存在,我需要创建文档的另一种方法添加到单独的对象集合中。

[另一个更新 x2] --> 从我一直在阅读和从各种来源获得的内容 - 这是为此正确创建事务的唯一方法 - 是“findOneAndModify”文档以使用某些字段将其标记为脏肯定会更新,例如使用 objectId “锁定”(因为这永远不会导致 NO-OP - 即,它肯定会导致更改)。

如果另一个操作尝试写入它,Mongo 现在可以检测到该记录已经是事务的一部分。

因此,任何写入它的东西都会在其他操作上导致 writeError。然后我的事务可以慢慢地处理该记录并锁定它。当它写出并提交时,该记录绝对不会被其他任何东西触及。如果由于某种原因没有交易就无法做到这一点,那么我是否在这里以最简单的方式创建交易?

4

1 回答 1

1

使用 Mongo 的交易是“正确”的方式,但我将提供一个足够的简单解决方案(有一些警告)。

最简单的解决方案是使用findOneAndUpdate来读取文档并更新一个新字段,我们称之为它status,因为它是原子的,这是可能的。

查询看起来像这样:

const doc = await db.collection.findOneAndUpdate(
    {
        _id: docId,
        status: { $ne: 'processing' }
    },
    {
        $set: {
            status: 'processing'
        }
    }
);

所以如果dov.valuenull那么它意味着(假设文档存在)另一个进程正在处理它。完成处理后,您只需重置status为任何其他值。

现在,因为在进程完成之前,您本质上会锁定此文档以防被读取,因此您必须确保处理整个过程中抛出的错误、更新失败、数据库连接问题等情况。

总的来说,我会谨慎使用这种方法,因为它只会为“正确”查询“锁定”文档(每个进程都需要更新才能使用该status字段),这有点问题,具体取决于您的用例。

于 2021-03-21T15:35:12.810 回答