Rubyが好きなプログラマーの日記。日々の生活、開発に関するメモとか考えとか。
パフォーマンスに影響する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レコードまでロードしているため、パフォーマンスが良くない(もしくは悪くなる)ようだ。
なので、そういったことを回避するために、後者のようにscope
やwhere
で絞り込んでからincludes
でロードすると良い。