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

Nei*_*eil 5 ruby activerecord join ruby-on-rails

除非我弄错了:joinsincludes在数据库级别有更好的性能:

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

一般来说,inner joina比a快subquery.

例:

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

#app/models/pet.rb
class Pet < ActiveRecord::Base
  belongs_to :owner
end
Run Code Online (Sandbox Code Playgroud)

使用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)
Run Code Online (Sandbox Code Playgroud)

现在使用joins哪个导致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"
Run Code Online (Sandbox Code Playgroud)

所以看起来使用它几乎总是更好joins,includes因为:

  • includes导致subquery(IN声明)
  • joins导致inner join通常比子查询更快的

然而,有一个问题与使用joins.这篇文章很好地描述了它.基本上,includes将所有关联的对象加载到内存中,这样,如果查询这些关联对象的任何属性,它就不会命中数据库.同时,joins不要将关联对象的属性加载到内存中,因此如果查询任何属性,它会对数据库进行额外的命中.

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

spi*_*ann 7

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

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

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

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

顺便说一句:Rails文档中有一个注释,它includes具有以下性能优势joins:

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