2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【第3回】eager_loadとpreloadの挙動の違いをまとめた

Last updated at Posted at 2021-07-02

前回の記事では、joinsleft_outer_joinsの挙動の違いについてまとめました。
前回の記事をまだ読んでいない方はこちらからどうぞ。
【第1回】RailsのN+1問題と解決するためのメソッド完全版
【第2回】Railsのjoinsとleft_outer_joinsの挙動と違いをまとめた

##本記事の内容

  1. joins
  2. left_outer_joins
  3. eager_load <=(本記事ではここから解説)
  4. preload
  5. includes

##eager_load
関連モデルを一括読み込みするメソッドです。
SQLのLEFT OUTER JOIN句を発行し関連付けをキャッシュします。
キャッシュとは一度取得したデータをメモリ上に一時的に保存する事で、次回のデータ取得時により高速にデータ取得する仕組みです。
eager_loadを使う事で、あるモデルを軸にその関連モデルの値を取得したい場合のN+1問題の解消ができる様になります。

例1
モデル名.eager_load(:関連モデル)

eager_loadは、関連モデルのデータをメモリに一時的に保存することで、メモリからデータを取得します。
関連モデルに存在する属性を、ループ処理の中で取得したい場合に使用します。

joinsleft_outer_joinsとの違いは、関連モデルを一括でキャッシュすることです。
###使い方

例2
User.eager_load(:post).each do |user|
 p user.posts.title
end

Userモデルと関連するPostモデルを一括読み込みしたうえでeachメソッドでループ処理を実行しています。
キャッシュされたPostモデルを使用するので、DBへのアクセスはされません。
そのためSQLが発行されず、N+1問題が解消されるわけですね。

##preload
同じく関連モデルを一括読み込みするメソッドです。
eager_loadとの違いは、SQLをモデルごとに発行し、関連モデルをキャッシュすることです。
つまり、eager_loadのSQL発行回数が1回に対して、preloadは軸となるモデルと関連モデルの2つに対してSQLを発行します。合計2回です。

preloadは対象テーブルのデータサイズが大きくJOINのコストが大きい場合に有効です。

###使い方

例1
モデル名.preload(:関連モデル)

関連モデルに存在する属性を、ループ処理の中で取得したい場合に使用します。

例2
User.preload(:post).each do |user|
 p user.posts.title
end

注意点としては、モデルごとにSQLを発行するので、eager_loadと異なり絞り込み条件を指定できません。

##includes
includesはSQL発行時の条件に合わせて、eager_loadpreloadどちらかを使用します。
N+1問題を解消するとき、とりあえずincludesを使っているという人も多いのではないでしょうか。

たしかに便利なメソッドですが、eager_loadpreloadのどちらの挙動を狙った実装なのかわからないので、最終的にパフォーマンスが落ちてしまう可能性があることに注意が必要です。
個人的にはincludesは非推奨です。eager_loadpreloadの挙動を理解したうえで実装するのがベストです。

###使い方

例1
モデル名.includes(:関連モデル)

includesメソッドでは以下の条件に合致する場合にはeager_loadの挙動を、合致しない場合はpreloadの挙動を行います。

  1. 引数に指定した関連テーブルに対しjoinsメソッドを使用している場合
  2. 引数に指定した関連テーブルに対しwhereメソッドを使用している場合
  3. 引数に指定した関連テーブルに対しreferencesメソッドを使用している場合
例2
class PostsController < ApplicationController

 def index
  @posts = Post.includes(:photos, :user).order('created_at DESC').page(params[:page]).per(3)
 end

end

このように実装した場合には、上記3つのメソッドは使用されていないのでpreloadが実行されているということになります。

##まとめ

  • eager_loadは、関連モデルのデータをメモリに一時的に保存することで、メモリからデータを取得する。
  • preloadは、SQLをモデルごとに発行し、関連モデルをキャッシュする。
  • includesはSQL発行時の条件に合わせて、eager_loadかpreloadどちらかを使用する。

ふぅー。やっとすべて解説できました。
「まだよくイメージがわかない」という人も手元の環境で色々試してみると、理解が加速するはずです。
ぜひ試してみてください。

2
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?