MongoDB:不能使用游标迭代所有数据

时间:2013-10-28 09:11:43

标签: java mongodb cursor

更新时更新:

解决!看到这个: MongoDB: cannot iterate through all data with cursor (because data is corrupted)

它是由损坏的数据集引起的。不是MongoDB或驱动程序。

=============================================== ==========================

我正在使用MongoDB(2.4.6)的最新Java驱动程序(2.11.3)。我有一个包含约250M记录的集合,我想使用游标迭代所有这些记录。但是,在10分钟左右之后,我得到了一个假的cursor.hasNext(),或者说服务器上没有光标的异常。

之后我学会了游标超时并用try / catch包装了我的cursor.next()。如果在遍历所有记录之前任何异常或hasNext()返回false,程序将关闭游标并分配一个新游标,然后再跳回上下文。

但后来我读到了关于cursor.skip()的性能问题......程序刚刚达到~20M记录,而cursor.skip()之后的cursor.next()抛出了“java.util.NoSuchElementException”。我相信这是因为跳过操作已经超时,这使光标无效。

是的我已经阅读过有关skip()性能问题和游标超时问题的内容......但是现在我认为我处于一个两难境地,即修复一个会破坏另一个。

那么,有没有办法优雅地遍历庞大数据集中的所有数据?

@mnemosyn已经指出我必须依赖基于范围的查询。但问题是我想将所有数据分成16个部分并在不同的机器上处理它们,并且数据不是均匀分布在任何单调的密钥空间内。如果需要负载平衡,必须有一种方法来计算特定范围内的密钥数量并平衡它们。我的目标是将它们分成16个部分,所以我必须找到四分位数的四分位数(对不起,我不知道是否有一个数学术语),并使用它们来分割数据。

有没有办法实现这个目标?

当通过获得分区边界键来实现第一次搜索时,我确实有一些想法。如果新光标再次超时,我可以简单地记录最新的tweetID并跳回新范围。但是,范围查询应该足够快,否则我仍然会超时。我对此并不自信......

更新

问题解决了!我没有意识到我不必以粗糙的方式分区数据。循环工作调度员会这样做。请参阅接受的答案中的评论。

1 个答案:

答案 0 :(得分:1)

总的来说,是的。如果你有一个单调场,理想情况下是一个索引场,你可以简单地走。例如,如果您使用ObjectId类型的字段作为主键,或者如果您有CreatedDate或其他内容,则只需使用$lt查询,即可获取固定数量的元素,然后使用您在上一批中遇到的最小$lt_id的{​​{1}}再次查询。

注意严格的单调行为与非严格的单调行为:如果密钥不严格,则可能必须使用CreatedDate,然后防止在欺骗中做两次。由于$lte字段是唯一的,_id始终是严格单调的。

如果你没有这样的钥匙,事情会有点棘手。您仍然可以沿着索引进行迭代(无论索引,无论是名称,哈希,UUID,Guid等)。这也很有效,但很难做快照,因为你永远不知道你刚刚开始遍历之前是否插入了刚刚找到的结果。此外,在遍历开始时插入文档时,将会遗漏这些文档。