N+1問題は「本来まとめて取れるはずのデータを、N件分バラバラに取りに行ってしまい、クエリが N+1 回に増えることでパフォーマンスが悪化する問題」です。
N+1問題とは何か?
- 親データを1回のSQLでN件取得する。
- そのN件それぞれについて、関連データを個別に取りに行くSQLを1回ずつ発行する。
- 結果として、合計クエリ数が
1 + Nになってしまう状況を指す。
例:記事一覧とコメントを表示したいコード。
$posts = Post::all(); // 1回のクエリ
foreach ($posts as $post) {
// 各 $post ごとに comments を取りに行くクエリが発行される(N回)
foreach ($post->comments as $comment) {
echo $comment->content;
}
}
この場合、記事が10件なら 1(posts) + 10(comments)= 11回クエリが発行されます。
これによってどんな問題が起こるのか
- クエリ回数がレコード数Nに比例して増えるため、データ量が増えるほどレスポンスが遅くなる。
- 短時間に大量のクエリが発行され、DBサーバーに高い負荷がかかる。
- データが増えた途端に画面1枚表示に数百〜数千クエリが飛ぶようになり、スケーラビリティが大きく損なわれる。
Laravelでの具体的な解決方法
Laravel/Eloquentでは、遅延ロード(Lazy Loading)のままリレーションをループ内で触るとN+1が発生します。
これを避ける代表的な手段が Eager Loading(積極的読み込み)です。