2013-05-02 148 views
14

我的用例如下 - 我有一个mongoDB中的文档集合,我必须发送它进行分析。 文件的格式是如下 -在MongoDB中批量FindAndModify的解决方案

{_id:物件( “517e769164702dacea7c40d8”), 日期: “1359911127494”, 状态: “可用”, other_fields ...}

我有一个阅读器过程,挑选前状态的前100个文档:可用排序方式日期并修改它们与状态:处理。 ReaderProcess发送文档进行分析。一旦分析完成,将状态更改为处理

目前阅读器过程首先获取由日期分类100个文档,然后更新状态处理在一个循环中的各个文件。这种情况下是否有更好的/有效的解决方案?

此外,在未来的可扩展性方面,我们可能会使用多个读取器进程。 在这种情况下,我希望一个阅读器进程拾取的100个文档不应该被另一个阅读器进程挑选。但现在读取和更新是单独的查询,所以很可能多个读取器进程选择相同的文档。

Bulk findAndModify(限制)将解决所有这些问题。但不幸的是,它在MongoDB中尚未提供。有没有解决这个问题的方法?

回答

12

正如你所说,目前没有干净的方式来做你想做的事情。在这个时候像一个你需要的操作,最好的方法是这样的:

  1. 读者选择具有相应的限制和排序
  2. 读者引号),它有自己独特的阅读器ID 1返回的文件X文件(e.g. 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只能保留当前可用的文档,并且写入是原子的)。如果您希望能够超时预订(例如,在读者可能崩溃/失败的情况下),我还会添加带有预留时间的时间戳。

编辑:更多细节:

所有的写操作可以偶尔产生的,如果写需要相当长的时间等待的操作。这意味着步骤2)可能看不到),步长为1标注的所有文件,除非你采取以下步骤:

  • 使用合适的“W”(写入关注)值,这意味着1或更高。这将确保调用写操作的连接将等待它完成,而不管它是否成功。
  • 请确保您在第2步中对相同连接(只与启用了slaveOk的读取的复制数据库相关)或线程执行读取操作,以确保它们是连续的。前者可以在大多数驱动程序中使用“requestStart”和“requestDone”方法或类似方法完成(Java文档here)。
    • 将$ isolated标志添加到您的多重更新中,以确保它不能与其他写入操作交错。

也看到有关原子/隔离的讨论意见。我错误地认为多个更新是孤立的。他们不是,或者至少不是默认的。

+0

您已写入的写入是原子的,我假设为单个文档写入,因为使用多标志true的第二步更新将不会是原子的,除非与隔离一起使用(仅适用于未分发的设置)。纠正我,如果我错了。除了那个伟大的答案。 – ameykpatil 2013-05-02 13:02:01

+0

没有整个写操作是原子的。所以如果你更新了100个文件,我建议整个操作都是原子的。它是安全的。 – 2013-05-02 13:31:07

+0

你确定MongoDB的原子多文档更新吗?它是安全分割的,但这是因为分片以特定的方式工作,并且mongos将路由操作传送到集合中合适的mongod。 – Sammaye 2013-05-02 13:41:20