MongoDB 文档操作是原子且隔离的,但它们是否一致?

发布于 2024-12-14 18:41:41 字数 427 浏览 0 评论 0原文

我正在将应用程序从 App Engine 数据存储区移植到 MongoDB 后端,并对“文档更新”的一致性有疑问。我知道一个文档的更新都是原子的和隔离的,但是有没有办法保证它们在不同的副本集之间“一致”?

在我们的应用程序中,许多用户可以(并且将会)尝试通过在一次更新期间插入一些嵌入文档(对象)来同时更新一个文档。我们需要确保这些更新在所有副本中以逻辑一致的方式发生,即当一个用户将一些嵌入文档“放入”父文档中时,其他用户不能将其嵌入文档放入父文档中,直到我们确保他们已经阅读并收到第一个用户的更新。

所以我所说的一致性的意思是,我们需要一种方法来确保,如果两个用户尝试同时对一个文档执行更新,MongoDB 只允许其中一个更新进行,并丢弃另一个(或至少防止两者发生)。我们在这里不能使用标准的“分片”解决方案,因为单个更新不仅仅包含增量或减量。

保证一份特定文档的一致性的最佳方法是什么?

I'm in the process of porting my application from an App Engine Datastore to a MongoDB backend and have a question regarding the consistency of "document updates." I understand that the updates on one document are all atomic and isolated, but is there a way to guarantee that they're "consistent" across different replica sets?

In our application, many users can (and will) be trying to update one document at the same time by inserting a few embedded documents (objects) into it during one single update. We need to ensure these updates occur in a logically consistent manner across all replicas, i.e. when one user "puts" a few embedded documents into the parent document, no other users can put their embedded documents in the parent document until we ensure they've read and received the first user's updates.

So what I mean by consistency is that we need a way to ensure that if two users attempt to perform an update on one document at exactly the same time, MongoDB only allows one of those updates to go through, and discards the other one (or at least prevents both from occuring). We can't use a standard "sharding" solution here, because a single update consists of more than just an increment or decrement.

What's the best way of guaranteeing the consistency of one particular document?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

不离久伴 2024-12-21 18:41:41

可能还有其他方法可以实现此目的,但一种方法是对文档进行版本控制,并仅针对用户之前阅读过的版本发布更新(即,确保自上次阅读以来没有其他人更新过该文档)。这是使用 pymongo 的此技术的一个简短示例:

>>> db.foo.save({'_id': 'a', 'version': 1, 'things': []}, safe=True)
'a'
>>> db.foo.update({'_id': 'a', 'version': 1}, {'$push': {'things': 'thing1'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': True, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 1}

请注意,上面的键“n”为 1,表明文档已

>>> db.foo.update({'_id': 'a', 'version': 1}, {'$push': {'things': 'thing2'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': False, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 0}

在此处更新,我们尝试针对错误的版本进行更新,键“n”为 0

>>> db.foo.update({'_id': 'a', 'version': 2}, {'$push': {'things': 'thing2'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': True, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 1}
>>> db.foo.find_one()
{'things': ['thing1', 'thing2'], '_id': 'a', 'version': 3}

请注意,此技术依赖于使用安全写入,否则我们不会收到指示已更新文档数量的确认。对此的一种变体是使用 findAndModify 命令,该命令将返回如果没有找到与查询匹配的文档,则返回 None(在 Python 中)。 findAndModify 允许您返回文档的新版本(即应用更新后)或旧版本。

There may be other ways to accomplish this, but one approach is to version your documents, and issue updates against only the version that the user had previously read (i.e., ensure that no one else has updated the document since it was last read). Here's a brief example of this technique using pymongo:

>>> db.foo.save({'_id': 'a', 'version': 1, 'things': []}, safe=True)
'a'
>>> db.foo.update({'_id': 'a', 'version': 1}, {'$push': {'things': 'thing1'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': True, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 1}

note in the above, key "n" is 1, indicating that the document was updated

>>> db.foo.update({'_id': 'a', 'version': 1}, {'$push': {'things': 'thing2'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': False, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 0}

here where we tried to update against the wrong version, key "n" is 0

>>> db.foo.update({'_id': 'a', 'version': 2}, {'$push': {'things': 'thing2'}, '$inc': {'version': 1}}, safe=True)
{'updatedExisting': True, 'connectionId': 112, 'ok': 1.0, 'err': None, 'n': 1}
>>> db.foo.find_one()
{'things': ['thing1', 'thing2'], '_id': 'a', 'version': 3}

Note that this technique relies on using safe writes, otherwise we don't get an acknowledgement indicating the number of documents updated. A variation on this would use the findAndModify command, which will either return the document, or None (in Python) if no document matching the query was found. findAndModify allows you to return either the new (i.e. after updates are applied) or old version of the document.

橘虞初梦 2024-12-21 18:41:41

MongoDB 不提供主主复制或多版本并发。换句话说,写入总是到副本集中的同一台服务器。默认情况下,甚至禁用从辅助服务器的读取,因此默认行为是您一次仅与一台服务器通信。因此,如果使用原子修饰符(如$inc、$push等),则无需担心安全模式下结果不一致。

如果您不想将自己限制在这些原子修饰符上,请按照 dcrosta 的建议进行比较和交换(以及 mongo docs)看起来是个好主意。然而,所有这些都与副本集或分片无关 - 在单服务器场景中是相同的

如果您还需要在数据库/节点发生故障时确保读取一致性,则应确保在安全模式下写入大多数服务器。

如果允许不安全读取,这两种方法的行为会有所不同:原子更新操作仍然有效(但可能会产生意外结果),而比较和交换方法将会失败。

MongoDB does not offer master-master replication or multi-version concurrency. In other words, writes always go to the same server in a replica set. By default, even reads from secondaries are disabled so the default behavior is that you communicate only with one server at a time. Therefore, you do not need to worry about inconsistent results in safe mode if you use atomic modifiers (like $inc, $push, etc.).

If you don't want to restrict yourself to these atomic modifiers, compare and swap as recommended by dcrosta (and the mongo docs) looks like a good idea. All this is not related to replica sets or sharding, however - it would be the same in a single-server scenario.

If you need to ensure read consistency also in case of a database/node failure, you should make sure you're writing to the majority of servers in safe mode.

The two approaches behave different if you allow unsafe reads: the atomic update operations would still work (but may give unexpected results), while the compare-and-swap approach would fail.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文