我正在将我的应用程序从App Engine数据存储区移植到MongoDB后端,并对"文档更新"的一致性提出疑问.我知道一个文档的更新都是原子的和隔离的,但有没有办法保证它们在不同的副本集中"一致"?
在我们的应用程序中,许多用户可以(并且将会)在一次更新期间通过向其中插入一些嵌入文档(对象)来同时尝试更新一个文档.我们必须确保这些更新发生在所有副本逻辑一致的方式,即当一个用户"把"几嵌入文档到主文件,其他用户可以把自己嵌入文档的父文档中,直到我们确保他们已经阅读并收到第一个用户的更新.
所以我的意思是一致的是,我们需要一种方法来确保如果两个用户试图在一个文档进行更新恰好在同一时间,MongoDB中只允许这些更新要经过的一个,并丢弃另一个(或至少可以防止两者发生.我们不能在这里使用标准的"分片"解决方案,因为单个更新不仅仅包含增量或减量.
保证一个特定文档一致性的最佳方法是什么?
可能还有其他方法可以实现此目的,但一种方法是对文档进行版本控制,并仅针对用户之前读过的版本发布更新(即,确保自上次读取文档以来没有其他人更新过该文档).以下是使用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
允许您返回新的(即应用更新后)或旧版本的文档.