【问题标题】:MongoDB: cannot use a cursor to iterate through all the dataMongoDB:无法使用游标遍历所有数据
【发布时间】:2013-10-30 12:20:42
【问题描述】:

更新更新:

已解决!看到这个: 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(),或者说服务器上不存在游标的异常。

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

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

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

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

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

有没有办法做到这一点?

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

更新:

问题解决了!我没有意识到我不必以大块的方式对数据进行分区。循环作业调度员会做。请参阅已接受答案中的 cmets。

【问题讨论】:

  • 为什么处理 100 条记录需要很长时间才能获得如此大的批次?您可以制作第 1 批文档,然后只要您能够在不到 10 分钟的时间内处理它,您就可以获得下一个批次/文档。见docs.mongodb.org/manual/reference/method/cursor.batchSize
  • 我没有触摸 cursor.batchSize()。我只是封装了 100 条记录并将其推送到管道。我说“管道”是因为其中有 12 个,并且并行化使事情变得更快。不是我10分钟没碰光标。但相反,我一直在拉,拉,最后仍然超时。
  • @AsyaKamsky 看到这个:stackoverflow.com/questions/19649978/… 如果加快管道速度会有帮助,恕我直言,链接中的问题永远不会存在。
  • 不是真的。问题是您对结果所做的工作,而不是获取结果的速度。
  • 所以这和另一个问题是同一个问题?相同的损坏数据集?

标签: java mongodb cursor


【解决方案1】:

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

注意严格单调行为与非严格单调行为:如果密钥不严格,您可能必须使用$lte,然后防止对受骗者执行两次操作。由于_id 字段是唯一的,ObjectIds 始终是严格单调的。

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

【讨论】:

  • 记录是具有唯一 tweetID 为 _id 的推文。保证单调性。问题是我不知道如何控制批次的范围。现在,在获得 100 条记录后,我将它们放在一个列表中并将其推送到处理管道。由于管道运行速度非常慢,我必须在 16 台机器上启动管道实例并为它们分配不同的 FROM-TO 范围。我现在只知道记录的总数。但是这些记录并没有均匀地分布在 tweetID 或任何其他键中。因此,简单地将工作负载除以 ID 并不能提供良好的负载平衡......对此有什么建议吗?谢谢!
  • 是的,这不漂亮。我认为你需要一个两阶段的方法:第一阶段扫描数千个集合(大步骤,不返回整个数据,只是 id,必须是一个覆盖查询)。它可以立即开始排队工作(对许多机器上的二级工作人员),例如以循环方式。它继续迭代,所以它已经为第二阶段分配了几百万个工作。这可能只需要几分钟。您可以存储给定 id 的文档数量$lt,因为它永远不会改变。单调性太好了:)
  • 我知道了。目前我在每台管道机器上使用一个光标。正如你所建议的,我可以使用一台主机从 MongoDB 中获取/写入,以及作业调度。这将解决负载平衡问题。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-30
  • 1970-01-01
  • 1970-01-01
  • 2013-08-14
  • 2014-10-23
  • 2019-12-16
相关资源
最近更新 更多