N+1 でした。どう直しますか。
with() でリレーションを事前ロードします。ループ内で都度引いていた 1+N 本が、親1本と子の whereIn 1本の計2本にまとまります。再発防止は preventLazyLoading です。
面接官が見ているのはwith() を知っているか。単なる暗記か、仕組みを理解しているか。
これは「一覧ページの表示が重いと報告されました。Eloquent を使っています。最初に何をしますか。」への追撃質問です。
よくある答えと、面談でどう見られるか
- with() でリレーションを事前ロードする
1+N 本が2本になります。開発環境では Model::preventLazyLoading() を有効にして、遅延ロード時に例外が飛ぶようにしておくと再発を防げます。 - ループの中で使っているリレーションを毎回 fresh() で取り直す
かえってクエリが増えます。N+1 を悪化させる方向です。 - 一覧の取得件数を減らしてページングする
症状は軽くなりますが、1ページあたりで見れば N+1 は残ったままです。件数を増やせばまた顕在化します。 - DB のコネクションプールを増やす
本数の問題を並列度で殴る発想です。DB 側の負荷は減りません。
面接官は何を見ているか
- with() による Eager Loading を挙げた
- 「クエリが 1+N 本から 2 本になる」と仕組みで説明できた
- 再発防止(preventLazyLoading 等)に触れた
模範解答
リレーションを with() で事前ロードします。ループ内で都度引いていたものが、親の取得1本+子の whereIn 1本の計2本にまとまります。再発防止には Model::preventLazyLoading() を開発環境で有効にして、遅延ロードが起きたら例外を投げるようにしておくのが有効です。
ここからどう深掘りされるか
この問いに答えられた場合、面談は次の質問に進みます。
観点をすべて押さえられた場合
with() を入れたら今度はメモリ使用量が増えて、別のところで落ちました。なぜですか。どうしますか。