【いーがーろーでぃんぐ】

イーガーローディング とは?

最終更新:
💡 必要なデータをまとめて先取り、N+1問題の特効薬

ORMで、関連するデータを使う前にまとめて取得しておく手法。関連データを1件ずつ取りに行くN+1問題を防ぎ、データベースへのクエリ回数を減らせる。

📌 このページのポイント
N+1問題 vs イーガーローディング N+1問題(クエリが多い) 1回目: SELECT * FROM posts 2回目: SELECT * FROM users WHERE id=1 3回目: SELECT * FROM users WHERE id=2 4回目: SELECT * FROM users WHERE id=3 …N件分繰り返し 合計 N+1 回のクエリ 100件なら101回! イーガーローディング(IN句方式) 1回目: SELECT * FROM posts 2回目: SELECT * FROM users WHERE id IN(1,2,3…) 合計 2 回のクエリ 関連データを一括取得! 記事が100件でも2回 ORMのeager load / includes / with で事前に関連を読み込む 関連1つの場合の例。JOINで読み込む方式なら1回になる
N+1問題とイーガーローディングの比較
ひよこ ひよこ
イーガーローディングって何を積極的に読み込むの?
ペンギン先生 ペンギン先生
関連テーブルのデータだよ。たとえばブログ記事の一覧を取得するとき、各記事の著者情報も最初にまとめて取得しておくのがイーガーローディングなんだ
ひよこ ひよこ
まとめて取得しないとどうなるの?
ペンギン先生 ペンギン先生
記事が100件あったら、記事一覧で1回、各記事の著者を1件ずつ取りに行って100回、合計101回のクエリが走ることがある。これがN+1問題で、件数が増えるほど遅くなりやすいんだ
ひよこ ひよこ
イーガーローディングだと何回になるの?
ペンギン先生 ペンギン先生
著者という関連1つなら、IN句でまとめて取る方式は「記事で1回+著者で1回」の2回、JOINする方式なら1回だよ。RailsならArticle.includes(:author)、LaravelならArticle::with('author')->get() のように書くんだ
ひよこ ひよこ
includesはJOINとIN句のどっちを使うの?
ペンギン先生 ペンギン先生
Railsのincludesは状況に応じて選ぶよ。基本は別クエリで読み込むpreloadの動きで、関連テーブルの列を条件に使う場合などはLEFT OUTER JOINで読み込むeager_loadの動きになる。明示したいときはpreloadやeager_loadを直接使えるんだ
ひよこ ひよこ
全部イーガーにしちゃえばいいんじゃない?
ペンギン先生 ペンギン先生
使わない関連まで取得すると、メモリや時間の無駄になるよ。RailsのBulletというGemは、N+1だけでなく「読み込んだのに使われていないイーガーローディング」も開発中に知らせてくれるんだ。画面で本当に使う関連だけを指定するのが大事だね
もっと詳しく知りたい人へ

うっかりN+1を書いてしまうのを防ぐ方法はある?

フレームワークの機能で、遅延読み込みが起きたらエラーにできるよ。Railsではstrict_loadingを使うと、読み込んでいない関連にアクセスしたときにActiveRecord::StrictLoadingViolationErrorが発生する。LaravelではModel::preventLazyLoading()を設定すると、遅延読み込みでLazyLoadingViolationExceptionが発生する。公式ドキュメントの例のように本番以外の環境で有効にして、開発やテスト中に見つけるのが一般的な使い方だよ。

ペンギン
まとめ:ざっくりこれだけ覚えればOK!
「イーガーローディング」って出てきたら「関連データをまとめて先に読み込む手法」と思えればだいたいOK!
📖 おまけ:英語の意味
「Eager Loading」 = 積極的な読み込み
💬 Eager は「熱心な・積極的な」という意味で、先回りしてデータを読み込むイメージだよ

参考資料

← 用語集にもどる