ActiveRecord使用连接来提高性能,但是将所有相关记录加载到内存中,就像包含

时间:2015-06-29 00:10:30

标签: ruby-on-rails ruby join activerecord

除非我弄错了:joins的性能优于includes,因为在数据库级别:

  • joins会导致inner join
  • includes会导致subquery

通常,inner joinsubquery更快。

示例:

#app/models/owner.rb
class Owner < ActiveRecord::Base
  has_many :pets
end 

#app/models/pet.rb
class Pet < ActiveRecord::Base
  belongs_to :owner
end

使用rails console

# showing how 'includes' in rails causes an IN statement which is a subquery 
irb(main):001:0> @owners = Owner.all.includes(:pets)
Owner Load (2.7ms)  SELECT "owners".* FROM "owners"
Pet Load (0.4ms)  SELECT "pets".* FROM "pets" WHERE "pets"."owner_id" IN (1, 2, 3)

现在使用导致joins的{​​{1}}:

inner join

所以看起来使用irb(main):001:0> @owners = Owner.all.joins(:pets) Owner Load (0.3ms) SELECT "owners".* FROM "owners" INNER JOIN "pets" ON "pets"."owner_id" = "owners"."id" 几乎总是比joins更好,因为:

  • includes会导致includessubquery声明)
  • IN会导致joins通常比子查询
  • 更快

然而,使用inner join有一个问题。 This article does a great job describing it。基本上,joins将所有关联的对象加载到内存中,因此,如果查询这些关联对象的任何属性,它就不会命中数据库。同时,includes不会将关联对象的属性加载到内存中,因此如果查询任何属性,它会对数据库进行额外的命中。

所以这是我的问题:是否可以像joins一样进行内部联接以获得性能,但同时将所有相关对象加载到内存中,如joins那样? 换句话说:是否可以将所有关联的对象加载到像includes那样的内存中,但是会导致内部连接而不是子查询?

1 个答案:

答案 0 :(得分:7)

我认为你假设JOIN总是比两个查询更快是不正确的。它在很大程度上取决于数据库表的大小。

想象一下,您的数据库中有成千上万的拥有者和宠物。然后,即使您只想加载10条记录,您的数据库也必须首先加入所有数据库。另一方面,一个查询加载10个所有者和一个查询以加载该10个所有者的所有宠物将比JOIN更快。

我认为两种方法都可以解决不同的问题:

    当您需要组合两个表来对两个表的数据运行查询时,使用
  • joins
  • includes用于避免N + 1个查询。

顺便说一下:Rails documentation注明includes的效果优于joins

  

与简单连接相比,这通常会带来性能提升。