0

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

似乎是当 ActiveRecord::Relation 委托:each:to => :to_a( source )

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

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

在这个方法中发生了一堆过滤和查询find_task,但它最终返回了一个关系@tasks

然后,在模板中,我有:

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

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

它不会发生在所有:week_ids 上,只会发生在特定的 week_id 上,例如::week_id => 1

SQL 执行得很好,我得到了一个关系,这似乎是特定 ActiveRecord::Relation 上的可枚举问题。

更新

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

解释分析表明,所有扫描都是index scans如此,事实证明,查询执行得很好,需要大量的急切加载......我在合理的时间内得到了一个急切加载的“ActiveRecord::Relation”。

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

 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然后
  • 一个包含大量 , 的大型 SQL 查询LEFT OUTER JOINS in 33,000 ms(很长,但不是大多数)
  • 257,000 ms在模板中。

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

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

4

2 回答 2

1

问题在于急切加载。在 ActiveRecord::Relation 实例上调用可枚举方法时,它会被委托给.to_a,这可能需要很长时间才能处理大量关系。即使我在循环@tasks,我也急切地加载了这么多.to_a花费太长时间的对象。

我的短期解决方案是简单地急切加载更少的对象,即使它最终会用 n+1 个查询伤害我。

于 2012-10-19T05:03:40.717 回答
1

我知道这是一个旧帖子,但为了将来参考,这将通过使用find_each来解决。

您可以在Ruby on Rails 指南中找到更多信息。

于 2015-01-26T19:42:14.677 回答