MongoDB - 选择文档进行更新 - 选择后没有其他操作修改同一文档
Posted
技术标签:
【中文标题】MongoDB - 选择文档进行更新 - 选择后没有其他操作修改同一文档【英文标题】:MongoDB - select document for update - without another operation modifying the same document after the select 【发布时间】:2021-06-18 09:19:16 【问题描述】:我有一份文件需要阅读和更新。同时,很可能另一个进程也在做同样的事情,这会破坏文档更新。
例如,如果进程 A 读取文档 d 并在其中添加字段 'a' 并写入文档,并且进程 B 在进程 A 写入之前读取文档 d,并添加字段 b 并写入文档,那么无论哪个进程写入输出的更改将得到它们的更改,因为它破坏了第一个编写的更改。
我已经阅读了这篇文章以及其他一些关于 mongo 的非常复杂的事务文章。有人可以描述一个简单的解决方案吗?我还没有遇到让我对此感到满意的东西。
https://www.mongodb.com/blog/post/how-to-select--for-update-inside-mongodb-transactions
[更新]- 此外,我正在尝试扩充一个可能尚不存在的文档。如果文档不存在,我需要创建它。我还需要阅读它来分析它。一个关键是“relatedIds”(一个数组)。如果在其中找不到 id,我会推送到该数组。如果文档不存在,我需要创建文档的另一种方法添加到单独的对象集合中。
[ANOTHER UPDATE x2] --> 根据我一直在阅读和从各种来源获得的信息 - 正确为此创建事务的唯一方法是“findOneAndModify”文档以将其标记为脏某些肯定会更新的字段,例如带有 objectId 的“锁定”(因为这永远不会导致 NO-OP - 即,它肯定会导致更改)。
如果另一个操作尝试写入它,Mongo 现在可以检测到该记录已经是事务的一部分。
因此,任何写入它的内容都会在其他操作上导致 writeError。然后我的事务可以慢慢地处理该记录并锁定它。当它写出并提交时,该记录绝对不会被其他任何东西触及。如果由于某种原因没有交易就无法做到这一点,那么我是否在这里以最简单的方式创建交易?
【问题讨论】:
【参考方案1】:使用 Mongo 的交易是“正确”的方式,但我会提供一个简单的解决方案就足够了(有一些警告)。
最简单的解决方案是使用findOneAndUpdate 读取文档并更新一个新字段,我们称之为status
,因为它是atomic 这是可能的。
查询看起来像这样:
const doc = await db.collection.findOneAndUpdate(
_id: docId,
status: $ne: 'processing'
,
$set:
status: 'processing'
);
所以如果dov.value
是null
则意味着(假设文档存在)另一个进程正在处理它。完成处理后,您只需将 status
重置为任何其他值。
现在,由于在进程完成之前,您本质上会锁定此文档以防止被读取,因此您必须确保处理诸如在整个过程中引发的错误、更新失败、数据库连接问题等情况。
总的来说,我会谨慎使用这种方法,因为它只会为“正确”查询“锁定”文档(每个进程都需要更新以使用 status
字段),这有点问题,取决于您的用例。
【讨论】:
这很有趣!我正在更新问题以添加一些我认为阻止我使用您的解决方案的附加信息。我想 finally 子句需要删除“处理”以确保它不会永远被锁定。如果在我到达 finally 之前停电了怎么办? 如果我理解正确,在一个事务中,如果我阅读了这个文档然后更新它并提交,如果我阅读的文档在我阅读后被修改了,我会得到一个异常? “快速而肮脏”的方法的一个问题是它对此类问题没有弹性 我在上面添加了另一个更新编辑。如果有机会,感谢您的关注!以上是关于MongoDB - 选择文档进行更新 - 选择后没有其他操作修改同一文档的主要内容,如果未能解决你的问题,请参考以下文章
如何选择然后对 MongoDB 文档进行排序,然后找到下一个要挖掘的文档?