MongoDB批量FindAndModify解决方案

16
我的用例如下 - 我有一个存储在mongoDB中的文档集合,我需要将它们发送进行分析。 这些文档的格式如下 -
{ _id:ObjectId("517e769164702dacea7c40d8") , date:"1359911127494", status:"available", other_fields... }
我有一个读取器进程,它会选择状态为available 的前100个文档,并按日期排序并使用处理中修改它们。 ReaderProcess会将文档发送进行分析。分析完成后,将更改状态为已处理。
目前读取器进程首先按日期获取100个文档,然后在循环中为每个文档更新状态为处理中。是否有更好/更有效的解决方案?
此外,为了可扩展性,我们可能会采用多个阅读器进程。 在这种情况下,我希望一个读取器进程选取的100个文档不应由另一个读取器进程选取。 但是,获取和更新是分开的查询,因此很有可能多个读取器进程选择相同的文档。
如果MongoDB提供了限制的批量findAndModify,则可以解决所有这些问题。但不幸的是,MongoDB还没有提供此功能。是否有解决此问题的其他解决方案?
1个回答

13

正如您所提到的,目前没有干净的方法来完成您想要的操作。目前对于您需要的操作最好的方法是:

  1. 阅读器使用适当的限制和排序选择X个文档
  2. 阅读器使用自己的唯一阅读器ID标记由1)返回的文档(例如:update({_id:{$in:[<result set ids>]}, state:"available", $isolated:1}, {$set:{readerId:<your reader's ID>, state:"processing"}}, false, true))
  3. 阅读器选择所有被标记为正在处理中并且具有自己阅读器ID的文档。此时可以保证您独占该结果集。
  4. 将第3步的结果集提供给您的处理。

请注意,在高度并发的情况下,这个方法也适用,因为阅读器不能预订其他阅读器没有预订过的文档(请注意,步骤2只能预订当前可用的文档,并且写入是原子性的)。如果您要在预订时超时(例如对于阅读器可能崩溃/失败的情况),建议添加一个预订时间戳。

编辑:更多细节:

所有写操作都可以偶尔为挂起的操作让出控制权,如果写操作需要相对较长时间,则可能看不到步骤1标记的所有文档。因此,请按照以下步骤执行步骤2:

  • 使用适当的“w”(写关注)值,即1或更高。这将确保在调用写操作的连接上等待它完成,而不管它是否让出控制权。
  • 确保在相同的连接(仅适用于启用了slaveOk读取的副本集)或线程上执行步骤2中的读取操作,以便它们被保证是顺序的。前者可以在大多数驱动程序中使用“requestStart”和“requestDone”方法或类似方法来完成(Java文档在这里)。
    • 将$isolated标志添加到您的多个更新中,以确保它不能与其他写操作交错。

还请参见评论中有关原子性/隔离性的讨论。我错误地认为多个更新是隔离的。它们不是,或者至少默认情况下不是。


你确定MongoDB支持原子多文档更新吗?它是分片安全的,但这是因为分片以特定方式工作,mongos将操作路由到集合中适当的mongod。 - Sammaye
@RemonvanVliet 我认为你需要使用$isolated操作符来确保多更新不会与其他写操作交错执行。 - JohnnyHK
1
我改口了。“原子性”并不是一开始的合适术语,但我确实认为写操作都是隔离的。我看到10gen也继续保持其传统,将最不明显/最冒险的标志值作为默认值。我会相应地调整我的答案。我认为这并不会真正影响建议的实现,因为即使在同一数据上交错更新,也仍然会导致单个文档被保留给单个读者。 - Remon van Vliet
在第二步中,您是否可以完全放弃“state”,并仅查询{_id:{$in:[<result set ids>]}, readerID: null, $isolated:1}? - Andrew Rasmussen
你可以这样做,但这是原始问题的一部分要求。 - Remon van Vliet
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接