在ActiveRecord中预加载完整的深层关系集

时间:2014-07-10 09:35:31

标签: ruby-on-rails ruby-on-rails-3 performance activerecord

this question开始,我的情况是我的Rails 3应用程序中有一个对象的层次结构,可能是八个关系,主要是通过has_manybelongs_to关系,例如:project has_many usersuser has_many permissionsproject has_many nodesnode belongs_to permissionnode has_many propertiesproperty has_many edit_histories等等。它都是相当标准的东西,但它有相当多的东西。这是一个已经投入生产的应用程序,因此架构是我目前所依赖的。

我遇到的问题是,有时(例如,当我们打开一个项目时)我想急切地加载几乎所有东西,有时候我不会。因此,我不希望默认情况下所有内容都是急切加载的。当我确实需要所有内容时,尽管有一个严重的预加载列表,但我遇到了一些严重的N + 1问题,加载单个页面正在制定数百个查询,在某些情况下请求只是超时。使用Bullet没有在我的关系中显示任何明显缺失的include语句,但是当我查看正在生成的SQL时,我注意到的是一些巨大的" IN"查询上有数百个ID - 尽管它们使用主键,但这似乎是一种检索数据的笨重方式。

如果我从头开始写这样的东西,我可能会把一个复杂的SQL语句放在一起,一次性取出所有数据(根据我知道的一些事情,但ActiveRecord可能不会)然后匹配之后的关系。显然,这会打破整个数据库不可知的事情,但是现在页面超时会给我们的用户带来更多麻烦,即使他们确实关心Rails应用程序的纯度,他们也不会这样做。我的问题是:

有没有办法在ActiveRecord或可以提供帮助的宝石中做这样的事情?有没有其他方法可以最大限度地减少执行的查询数量或任何其他技巧,以便及时恢复这种分层数据?

1 个答案:

答案 0 :(得分:2)

如果你只是使用:includes,rails会选择一个包含策略,这不是你想要的。这可能会导致您急需加载实际上不需要的东西。渴望通过id来加载相关记录只是它选择的一个选项。有时它比加入更好,有时候没有,有时它包括你不想要的东西。

请参阅Tricks Rails didn't tell you about #Eager load Strategies。

这里没有宝石可以提供帮助,因为这是一个非常特例的问题。 Plz发布另一个问题,有一个特定的问题,描述了应该在页面上显示的确切数据以及你的模型如何表现出来。请提供一些伪代码,显示Active Record数据的消耗,这会生成n + 1个查询。

另外,我可以说rails在处理大型数据集时非常糟糕。也许你应该只显示更少的条目,甚至使用自定义的SQL查询,如果这不是选项

编辑1: 解决有关可选预先加载的问题的示例

#class Project

#scope
def self.with_full_users
  includes(:users => {:permisions => :nodes})
end