Satoryu's Diary

Rubyが好きなプログラマーの日記。日々の生活、開発に関するメモとか考えとか。


2015年03月12日 [長年日記]

_ ActiveRecord::QueryMethods#includes は差し込む順序にご用心

パフォーマンスに影響するN+1問題を解決するために、bullet を入れて、問題が起きている箇所を検出しつつ修正してみたんだけど、逆に悪くなったりしていた。 Rails のログを見ながら試してみたところ、Eager Loadする includes を差し込むと、その時点での対象を全てロードするということが原因だということがわかった。

例えば、

class Post < ActiveRecord::Base
  scope :published, -> { where(public: true) }
  has_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :post
end

のようなモデルがあった時に、

  • Post.includes(:comments).published
  • Post.published.includes(:comments)

とでは結果は同じであるが、両者ともにN+1問題を解決しているものの、前者の方がパフォーマンスが悪い。これは、Post.includes(:comments) とした時点でEager Loadingが実施されるため、この時点だと、Post.all に関連するComment をロードし、その後にpublished スコープでPostを絞り込んでいる。余計なCommentレコードまでロードしているため、パフォーマンスが良くない(もしくは悪くなる)ようだ。 なので、そういったことを回避するために、後者のようにscopewhereで絞り込んでからincludes でロードすると良い。

参考


最近の投稿

翻訳しました(ちょっとだけ)

follow us in feedly