Day 4・EloquentとリレーションとN+1問題
N+1問題
リレーションを持つデータをループで扱うと、SQLが件数分だけ増える定番の性能問題。
N+1問題とは
問題とは、一覧を取る1本のSQLに加えて、各行の関連データを取るために追加のSQLが件数分(N本)発行されてしまい、合計で「1 + N本」のクエリが走ってしまう性能問題です。Laravelに限らずORM全般で起きる、最も有名な落とし穴のひとつです。
なぜ起きるのか
原因は「遅延ロード」です。リレーションは、実際にアクセスされた瞬間に初めてSQLを発行します。投稿一覧を取り、ループの中で毎回「$post->user->name」のように著者にアクセスすると、投稿の件数だけ著者取得のSQLが繰り返されてしまいます。
悪い例(N+1が発生する)
投稿100件なら、一覧1本 + 著者取得100本 = 合計101本のSQLが発行される。
// 投稿を全件取得(SQLは1本)
$posts = Post::all();
foreach ($posts as $post) {
// ループの中で著者にアクセス → 1件ごとにSQLが1本発行される!
echo $post->user->name;
}
// 投稿100件なら 1 + 100 = 101本のクエリ良い例(Eager Loadingで解決)
with で著者を事前にまとめて取得。一覧1本 + 著者一括1本 = 合計2本で済む。
// with('user') で著者を事前にまとめて読み込む([[eager-loading]])
$posts = Post::with('user')->get();
foreach ($posts as $post) {
// すでに読み込み済みなので、ここでは追加のSQLは出ない
echo $post->user->name;
}
// 投稿が何件でも 1 + 1 = 2本のクエリで済むなぜ致命的になりやすいのか
件数が少ないうちは動いてしまうため、開発中は気づきにくいのがN+1の怖さです。本番でデータが増えると、101本・1001本とクエリが膨れ、ページ表示が急に遅くなります。1本1本は速くても、DBへの往復回数そのものが増えるのがボトルネックです。『動くけど遅い』の典型で、レビューや設計段階で潰すべき問題です。
-- 最初の1本: 一覧
SELECT * FROM posts;
-- 続く N 本: 行ごとに繰り返される
SELECT * FROM users WHERE id = 1;
SELECT * FROM users WHERE id = 2;
SELECT * FROM users WHERE id = 3;
-- … 投稿の件数だけ延々と続く …-- 1本目: 一覧
SELECT * FROM posts;
-- 2本目: 必要な著者を IN でまとめて1回だけ取得
SELECT * FROM users WHERE id IN (1, 2, 3, 4, 5);どう気づくか
N+1は「発行されているSQLの本数」を見れば一発で分かります。以下のような方法で、1画面あたり何本のクエリが走っているかを可視化します。
- Laravel Debugbar: 画面下部に、そのページで発行されたクエリ本数と内容を表示してくれる。同じSQLが大量に並んでいたらN+1のサイン。
- Laravel Telescope: 開発用の監視ツールで、リクエストごとのクエリを一覧・件数で確認できる。
- DB::listen やクエリログ: 発行SQLをログに出して数える。
- Model::preventLazyLoading(): 遅延ロードが起きたら例外を投げさせ、開発中にN+1を強制的に検知する。
補足
コツは「同じ形のSELECTが件数分だけ並んでいないか」を見ることです。WHERE id = ? の値だけ違うSQLが延々と続いていたら、ほぼN+1だと判断できます。
面接ではこう言う
「N+1問題は、一覧の1本に加えて各行の関連取得でN本のSQLが出て、合計1+N本になる性能問題です。遅延ロードのままループ内でリレーションにアクセスすると起きます。私はDebugbarやTelescopeで発行クエリ本数を確認して気づき、withによるEager Loadingで1+1本に抑えて解決します」と、原因・気づき方・解決までワンセットで話せると強いです。
読んでも腑に落ちない所は、AIに噛み砕いてもらいましょう。
理解度チェック
答えられれば、面接で説明できる状態に一歩近づきます。
N+1問題が起きる直接の原因として、最も適切なものはどれか。
投稿100件それぞれの著者名を、遅延ロードのまま表示した。発行されるクエリ本数に最も近いものはどれか。
N+1問題とは何か、なぜ起きるか、どうやって気づくかを、面接官に説明するつもりで述べよ。
記述式です。まず自分の言葉で答えを考えてから、模範解答を開いて自己採点してください。