使用rails 3和mongoDB与mongoid适配器,如何批量查找到mongo DB?我需要获取特定mongo数据库集合中的所有记录,并在solr中索引它们(搜索数据的初始索引).
我遇到的问题是,做Model.all会抓取所有记录并将它们存储到内存中.然后当我处理它们并在solr中索引时,我的内存被吃掉了,进程就死了.
我要做的是在mongo中批量查找,这样我就可以一次迭代超过1,000条记录,将它们传递给solr进行索引,然后处理下一个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中进行批量查找时,文档很稀疏.我看到很多关于批量插入但没有批量查找的文档.
使用Mongoid,您无需手动批处理查询.
在Mongoid中,Model.all
返回一个Mongoid::Criteria
实例.在调用#each
此Criteria时,将实例化Mongo驱动程序游标并用于迭代记录.这个底层的Mongo驱动程序游标已经批处理所有记录.默认情况下batch_size
为100.
有关此主题的更多信息,请阅读Mongoid作者和维护者的评论.
总之,您可以这样做:
Model.all.each do |r| Sunspot.index(r) end
如果要遍历每个记录都需要大量处理的集合(即查询每个项目的外部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
这是您可以用来添加批处理功能的辅助方法。可以这样使用:
Post.all.order_by(:id => 1).in_batches_of(7).each_with_index do |post, index| # call external slow API end
只要确保您的查询上始终有一个order_by。否则,分页可能无法完成您想要的操作。我也坚持使用100个或更少的批次。如接受的答案中所述,Mongoid以100批为单位进行查询,因此您永远都不想在进行处理时使光标保持打开状态。
将批次发送到太阳黑子的速度也更快.我是这样做的:
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
no_timeout
:防止光标断开(默认情况下10分钟后)
only
:仅选择实际编制索引的id和字段
batch_size
:获取1000个条目而不是100个条目