第14回: N+1をSQL側で防ぐ — ループ取得の罠を設計で断ち切る

章: 第3章: パフォーマンス最適化

一覧ページが遅い原因、N+1になっていませんか?

ユーザー一覧を表示するたびに、各ユーザーの投稿を個別に取得している——そんな実装を見たことはないでしょうか。これがN+1問題です。10件なら10回、1000件なら1001回のクエリが走り、データ量の増加とともに急激にレスポンスが悪化します。

N+1はなぜ起きるのか

アプリケーション側でループを使って関連データを1件ずつ取得するコードは、シンプルに見えて危険です。ORMを使っていても、リレーション取得を明示的に制御しなければN+1になります。根本的な解決はSQL側で一括取得する設計です。

N+1発生パターンと改善策の比較

パターン SQL発行回数 問題
ループで個別SELECT(N+1) N+1回(投稿数+1) データ量増加で線形に悪化
JOIN で一括取得(良い例) 1回 安定したパフォーマンス
IN句で一括取得(良い例) 2回(ID取得+一括取得) ORMのEager Loadと相性が良い

チェックポイント: 一覧系APIを書くときは「ループの中でSELECTしていないか」を必ず確認しましょう。スロークエリログや実行回数のモニタリングで、同じパターンのクエリが大量に並んでいたらN+1のサインです。

コードサンプル


SELECT p.id, p.title, u.name
FROM posts p
JOIN users u ON u.id = p.user_id
WHERE p.id IN (101, 102, 103, 104);

まとめ & 次のステップ

  • N+1はアプリ側ループでの個別取得が原因で、データ量増加で急激に遅くなります
  • 一覧APIはまずJOINやIN句で一括取得できないか設計段階で検討しましょう
  • EXPLAINで実行回数と行数を確認し、改善前後を比較して記録します
  • ORMを使う場合もEager Loadの設定を明示するクセをつけましょう
  • スロークエリログで同一パターンの大量発行を早期に発見できます

次回は クエリチューニングの手順 を学びます。「計測→仮説→検証」の手順を体系化し、再現性のある改善サイクルを回せるようになりましょう。

Related Articles