Ruby on rails 批量查找mongoDB记录(使用mongoid ruby适配器)
使用带有mongoid适配器的rails 3和mongoDB,如何批量查找mongoDB?我需要获取特定mongo DB集合中的所有记录,并在solr(用于搜索的数据初始索引)中对它们进行索引 我遇到的问题是执行Model.all会获取所有记录并将其存储到内存中。然后,当我对它们进行处理并在solr中建立索引时,我的内存被消耗殆尽,这个过程也就结束了 我想做的是在mongo中批量查找,这样我可以一次迭代1000条记录,将它们传递给solr到index,然后处理下一个1000条记录,以此类推 我目前拥有的代码实现了以下功能:Ruby on rails 批量查找mongoDB记录(使用mongoid ruby适配器),ruby-on-rails,mongodb,mongoid,Ruby On Rails,Mongodb,Mongoid,使用带有mongoid适配器的rails 3和mongoDB,如何批量查找mongoDB?我需要获取特定mongo DB集合中的所有记录,并在solr(用于搜索的数据初始索引)中对它们进行索引 我遇到的问题是执行Model.all会获取所有记录并将其存储到内存中。然后,当我对它们进行处理并在solr中建立索引时,我的内存被消耗殆尽,这个过程也就结束了 我想做的是在mongo中批量查找,这样我可以一次迭代1000条记录,将它们传递给solr到index,然后处理下一个1000条记录,以此类推 我目
Model.all.each do |r|
Sunspot.index(r)
end
对于一个拥有大约150万条记录的集合,这会占用8+GB的内存并终止进程。在ActiveRecord中,有一个find_In_batches方法,允许我将查询分为可管理的批,以防止内存失控。然而,对于mongoDB/mongoid,我似乎找不到类似的东西
我希望能够做到以下几点:
Model.all.in_batches_of(1000) do |batch|
Sunpot.index(batch)
end
这将减轻我的记忆问题和查询困难,每次只做一个可管理的问题集。然而,在mongoDB中进行批处理查找的文档很少。我看到了很多关于批插入的文档,但没有批查找的文档。我不确定批处理,但您可以这样做
current_page = 0
item_count = Model.count
while item_count > 0
Model.all.skip(current_page * 1000).limit(1000).each do |item|
Sunpot.index(item)
end
item_count-=1000
current_page+=1
end
但是如果你正在寻找一个完美的长期解决方案,我不会推荐这个。让我解释一下我是如何在我的应用程序中处理相同的场景的。而不是做批量作业
- 我创建了一个更新solr索引的作业
结束class SolrUpdator @queue = :solr_updator def self.perform(item_id) item = Model.find(item_id) #i have used RSolr, u can change the below code to handle sunspot solr = RSolr.connect :url => Rails.application.config.solr_path js = JSON.parse(item.to_json) solr.add js end
- 添加项目后,我只是将一个条目放入resque队列
Resque.enqueue(SolrUpdator, item.id.to_s)
- 就这么多,重新开始,一切都会好起来的
Model.all
返回一个Mongoid::Criteria
实例。根据此条件调用#each
,Mongo驱动程序游标将被实例化并用于迭代记录。此基础Mongo驱动程序游标已批处理所有记录。默认情况下,批次大小为100
有关此主题的更多信息,请阅读
总之,您可以这样做:
Model.all.each do |r|
Sunspot.index(r)
end
正如@RyanMcGeary所说,您不需要担心批处理查询。但是,一次索引一个对象要比批处理它们慢得多
Model.all.to_a.in_groups_of(1000, false) do |records|
Sunspot.index! records
end
向太阳黑子发送批次也会更快。
我就是这样做的:
records = []
Model.batch_size(1000).no_timeout.only(:your_text_field, :_id).all.each do |r|
records << r
if records.size > 1000
Sunspot.index! records
records.clear
end
end
Sunspot.index! records
记录=[]
型号。批量大小(1000)。无超时。仅(:您的文本字段,:\u id)。全部。每个都有|
记录1000
太阳黑子指数!记录
记录,清除
结束
结束
太阳黑子指数!记录
无超时
:防止光标断开连接(默认情况下,10分钟后)
仅
:仅选择实际索引的id和字段
batch\u size
:获取1000个条目,而不是100个条目如果在每个记录都需要大量处理(即查询每个项目的外部API)的集合上进行迭代,则光标可能会超时。在这种情况下,您需要执行多个查询,以避免光标处于打开状态
require 'mongoid'
module Mongoid
class Criteria
def in_batches_of(count = 100)
Enumerator.new do |y|
total = 0
loop do
batch = 0
self.limit(count).skip(total).each do |item|
total += 1
batch += 1
y << item
end
break if batch == 0
end
end
end
end
end
只要确保您的查询中始终有一个order\u by即可。否则,分页可能无法实现您希望的功能。此外,我会坚持批量100或更少。正如在接受答案中所说的,Mongoid查询以100个为一批,因此在处理过程中,您永远不想让光标保持打开状态。以下内容适用于您,请尝试一下
Model.all.in_groups_of(1000, false) do |r|
Sunspot.index! r
end
Ramesh,您提供的第一块代码非常适合我的用例。它只是使用脚本文件一次性加载和索引数据,所以对于我的特殊情况,使用resque可能会有点过头。但是批处理功能非常有效!这是没有必要的。Mongoid和底层Mongo驱动程序已经使用游标批处理查询。这将使内存占用空间保持较小。您确定这会导致内存问题吗?Mongoid和底层Mongo驱动程序已经使用游标批处理查询。这样可以减少内存占用。顺便说一句,您应该将已接受的答案更改为@RyanMcGeary的答案-然后您的问题的所有未来访问都将看到正确的答案,并且没有人不会执行驱动程序已经完成的手动优化。感谢@RyanMcGeary提供的信息,天哪,我怎么会错过光标的事情,,,在durran指定的关于批处理大小的链接中,我们如何指定外部…?@Edmund“Hit”可能不是这里使用的最佳词语,因为它意味着每次都要重新运行查询。这是一个数据库游标。想想看,这更像是成批地将数据传输到100个数据中。@RyanMcGeary,你答案中的链接被破坏了。你能编辑/更正吗?@p.matsinopoulos我花了一段时间才找到相同的评论。将近5年了,Mongoid从GitHub问题转向了JIRA问题。我想我找到了合适的注释。对于最新版本中的记录,批大小在内部通常从100开始,然后增加以减少对数据库的调用次数。它的优点还在于,它可以与所有可枚举方法一起使用,因此如果您希望以实际的ruby批处理(如100个数组)获取记录,您可以这样做:Model.all.each_slice(100){| array |…}
Model.all.to_a
将整个集合加载到内存中。没错,请不要这样做:当我们谈论大型数据集时,避免将整个集合一次性转换为数组:使用Model.find_each
或batch以任何方式,但决不要Model.all.to_a
Model.find_each不是Mongoid方法。您将使用Model.all.each代替。请记住“Sunspot.index!”!在循环后记录,否则您不会索引最后一组<1000我相信是正确的。我忘了复制这个部分。crite上的.no\u timeout
方法
Model.all.in_groups_of(1000, false) do |r|
Sunspot.index! r
end