在某些情况下,当我获得ActiveRecord关系时,我在ActiveRecord :: Relation上遇到.each
的奇怪行为
似乎是ActiveRecord :: Relation将:each
委托给:to => :to_a
(source)
@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 ms
,139 ms
然后LEFT OUTER JOINS in 33,000 ms
的大型SQL查询,(很长,但不是大多数的地方)然后257,000 ms
在模板中。当我复制模板中的行为时,我发现在to_a
关系上调用@tasks
需要大约3-4分钟。
所以,当我的服务器告诉我所有时间都花在模板上时,我可以看到在关系上调用一个可枚举的东西,是在查询执行时?即使在top
中我只能看到ruby
进程正在运行?
答案 0 :(得分:1)
我知道这是一篇旧帖子,但为了将来参考,我们可以使用find_each
来解决这个问题。
您可以在Ruby on Rails guides找到更多信息。
答案 1 :(得分:0)
原来问题在于急切加载。在ActiveRecord :: Relation实例上调用可枚举方法时,它会被委托给.to_a
,而在Ruby中,.to_a
方法可能会花费很长时间来处理大量关系。即使我正在循环@tasks
,我仍然急切地加载了.to_a
花费了太长时间的许多对象。
我的短期解决方案是简单地加载较少的对象。它最终会因为n + 1个查询而伤害我,但我.to_a
方法的时间不会太长。
这是我能想到的唯一解释。