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

更新更新:

解决了 ! 看到这个: MongoDB:无法用游标迭代所有数据(因为数据已损坏)

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

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

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

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

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

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

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

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

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

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

更新:

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

一般来说,是的。 如果你有一个单调字段,理想情况下是一个索引字段,你可以简单地沿着它走。 例如,如果您使用ObjectId类型的字段作为主键,或者如果您有CreatedDate或其他东西,您可以简单地使用$lt查询,获取固定数量的元素,然后使用$lt的最小_id再次查询或您在上一批中遇到的CreatedDate

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

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