2011-08-03 56 views
4

我有一个rake任务,需要遍历大量记录(称为商家),每个记录都有大量的关联项目。我的问题是,由于Rails自动缓存我的数据库查询的结果,我最终将我的工作人员放入交换空间很长时间。防止导致ActiveRecord查询结果缓存导致

总之,我想知道如何运行像这样的命令:没有缓存,每次通过“项目”的值

Merchant.all.each { |m| items = m.items }

我已经试过:

Merchant.all.each do |m|` 
    ActiveRecord::Base.connection.uncached do 
    items = m.items 
end 
end 

和我也尝试添加这对我的商家模式:

def items_uncached 
    self.class.uncached { items } 
end 

,然后调用items_uncached代替,但我最终仍费尽了内存使用情况以及我访问的每一组新项目。

我正在运行Rails 2.3.10,Ruby 1.9.2并使用Mysql进行存储。

在此先感谢您的想法!

*编辑:

这里是我工作的实际代码位:

File.open(output, "w") do |f| 
    Merchant.all.each do |m| 
    items = m.items 
    invalid_image_count = 0 
    items.each do |i| 
     invalid_image_count += 1 unless i.image_valid? 
    end 
    invalid_categories = items.select { |i| !i.categories_valid? }.count 
    f.puts "#{m.name} (#{m.id}): #{invalid_image_count} invalid images, " + 
      "#{invalid_categories} invalid categories" 
    end 
end 

试图做一些错误检查,然后记录结果。

+1

你想要做的事情在这里不是很清楚;也许你正在寻找像'ActiveRecord :: Base#find_each'? – coreyward

+0

好的,是的,我只是看着find_each,实际上可能会有所帮助。我会试试看。 – peter

+0

为了表达更好一点,当我在控制台中运行此代码时:Merchant.all.each {| m | items = m.items;打印“#{m.id}”},在每次迭代中,我的内存使用情况都会出现波动。我的猜测是,这是由于m.items产生1至10,000以上的ActiveRecords。 – peter

回答

3

如果你的公会是一个简单的has_many一个你可以试试这个:

Merchant.all.each do |m| 
    items = Item.find_all_by_merchant_id(m.id) 
    ... 
end 

甚至:

Merchant.find(:all, :select => "id, name").each do |m| 
    items = Item.find_all_by_merchant_id(m.id) 
    ... 
end 
+0

我不确定这是否会避免缓存。这是一个简单的has_many关系,但我认为在这种情况下,m.items应该与Item.find_all_by_merchant_id(m.id)具有相同的效果/返回值。这听起来正确吗? – peter

+0

我相信'm.items'在您释放'Merchant.all'数组之前不会被释放(即,直到您完成每个''循环),而在我的情况下,'items'将被释放并且在您重新分配之后收集垃圾下一个“每个”迭代。 –

+0

是的是的。你只是吹了我的脑海。谢谢。 – peter

4

查询缓存不是主要问题就在这里。无论如何,Rails“缓存”你的对象。

查询缓存只是一个“哈希查找”,它可以防止Rails不必要地点击数据库,它不控制ruby(或Rails)如何存储由关联在内部返回的对象。在each循环,这样,当你做m.items现在

m = Merhant.first # <- m is loaded from DB 
m.items   # <- items are loaded from DB and STORED(!) in m 
m.items   # <- items are returned from the association stored in m 
m.items.reload # <- hits the DB (or the query cache) 
m.instance_variable_get("@items") # <- returns the actual stored items 

您只需填充所有Merhcant情况下,与他们的所有项目,以及垃圾收集器不能:

例如试试这个(即使未缓存)释放任何东西,因为所有对象都在循环内时从all数组引用。

因此,解决方案就像Victor提出的那样,它可以防止“关联存储”触发。

+0

非常感谢您的进一步阐述。我现在得到它:) – peter