Rails积极记录关系到可枚举

时间:2012-10-18 18:41:29

标签: ruby-on-rails ruby ruby-on-rails-3.2 enumerable

在某些情况下,当我获得ActiveRecord关系时,我在ActiveRecord :: Relation上遇到.each的奇怪行为

似乎是ActiveRecord :: Relation将:each委托给:to => :to_asource

@tasks = Task.find_task(list, {:week_id => 1})

基本上,有一个冗长的类方法,它接受一个对象(list和一个带有:week_id的哈希

一堆过滤&查询在此find_task方法中发生,但最终会返回与@tasks

的关系

然后,在模板中,我有:

<% @tasks.each do |task| %>
.
.
.
<% end %>

无论出于何种原因,无论@tasks的大小如何,都需要约3分钟。我可以通过调用@tasks.to_a来复制同样的行为即使@tasks,ActiveRecord :: Relation的实例只有两个记录,在它们上面调用to_a需要&gt; 3分钟。

所有:week_id上都不会发生这种情况,仅在特定的week_id上发生,例如::week_id => 1

SQL执行正常,我得到一个关系,它似乎是一个特定的ActiveRecord :: Relation上可枚举的问题。

更新

在算法内部(我认为这意味着类方法)我做了很多急切的加载。所以Postgres做了很多LEFT OUTER JOIN s并且我已经将所有需要发生的表编入索引。

解释分析显示所有扫描都是index scans,并且事实证明查询执行得很好,并且需要大量的预期加载...并且我得到了一个渴望加载的'ActiveRecord :: Relation`。合理的时间。

更新2 虽然这个过程耗时3分钟,但我看到一个postgres进程运行了几秒钟,然后我在top中看到这个作为输出的3分钟:

 PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+    COMMAND                                                            
 8685 dylan     20   0 3407m 2.6g  904 R 99.7 69.1   1:14.49 /usr/local/bin/ruby script/rails s

当它最终完成时,服务器显示这个;

  • 急切加载:200 ms139 ms然后
  • 一个包含大量LEFT OUTER JOINS in 33,000 ms的大型SQL查询,(很长,但不是大多数的地方)然后
  • 257,000 ms在模板中。

当我复制模板中的行为时,我发现在to_a关系上调用@tasks需要大约3-4分钟。

所以,当我的服务器告诉我所有时间都花在模板上时,我可以看到在关系上调用一个可枚举的东西,是在查询执行时?即使在top中我只能看到ruby进程正在运行?

2 个答案:

答案 0 :(得分:1)

我知道这是一篇旧帖子,但为了将来参考,我们可以使用find_each来解决这个问题。

您可以在Ruby on Rails guides找到更多信息。

答案 1 :(得分:0)

原来问题在于急切加载。在ActiveRecord :: Relation实例上调用可枚举方法时,它会被委托给.to_a,而在Ruby中,.to_a方法可能会花费很长时间来处理大量关系。即使我正在循环@tasks,我仍然急切地加载了.to_a花费了太长时间的许多对象。

我的短期解决方案是简单地加载较少的对象。它最终会因为n + 1个查询而伤害我,但我.to_a方法的时间不会太长。

这是我能想到的唯一解释。