N+1問題によるクエリ発行数の増大を防ぐ方法|ORM別の検出・対策を徹底解説

データベースを使ったアプリケーション開発で、レスポンスが遅いと感じたことはないでしょうか。原因の多くは、気づかないうちに膨大な数のSQLクエリが発行されている N+1問題 です。 たとえばユーザー100人の一覧画面で、1回の全件取得+100回の関連テーブル参照、合計 101回 ものクエリが実行されるケースが典型的です。本来2回で済む処理が50倍以上に膨れ上がるため、データ量に比例してアプリケーション全体が劇的に遅くなります。 N+1問題とは何か リレーショナルデータベースでは、親テーブルと子テーブルを組み合わせてデータを取得する場面が頻繁にあります。N+1問題は、この関連データの取得時に発生するパフォーマンス上の欠陥です。 具体的な流れは次のとおりです。 親テーブルから全レコードを取得する(1回のクエリ) 取得した各レコードに対して、子テーブルへ個別にクエリを発行する(N回のクエリ) 合計で N+1回 のクエリが走ります。Nはレコード数なので、データが増えるほどクエリ発行数も線形に増加します。 クエリ発行数はなぜ問題になるのか クエリ1回ごとにネットワーク往復(ラウンドトリップ)が発生します。アプリケーションサーバーとデータベースサーバーが別マシンの場合、1往復あたり0.5〜2ms程度のオーバーヘッドが加わります。 レコード数 N+1時のクエリ数 JOINで解決した場合 差分(倍率) 10件 11回 1〜2回 約6〜11倍 100件 101回 1〜2回 約51〜101倍 1,000件 1,001回 1〜2回 約501〜1,001倍 10,000件 10,001回 1〜2回 約5,001〜10,001倍 ラウンドトリップだけで見ても、レコード1,000件で1秒以上のレイテンシ増が現実的に発生します。さらにデータベース側でもクエリパース・実行計画の策定がクエリ数だけ繰り返されるため、CPU負荷・コネクションプールの圧迫・ロック競合などが連鎖的に起こります。 SQLレベルで見るN+1問題の発生メカニズム ORMを使わない素のSQLでも、N+1問題の構造を理解しておくと対策が打ちやすくなります。ブログ記事とコメントのテーブルで見てみましょう。 -- テーブル定義 CREATE TABLE posts ( id SERIAL PRIMARY KEY, title VARCHAR(255) NOT NULL, body TEXT ); CREATE TABLE comments ( id SERIAL PRIMARY KEY, post_id INTEGER REFERENCES posts(id), content TEXT NOT NULL ); N+1が発生するクエリパターン -- 1回目:全記事を取得 SELECT * FROM posts; -- 記事ごとにコメントを取得(N回) SELECT * FROM comments WHERE post_id = 1; SELECT * FROM comments WHERE post_id = 2; SELECT * FROM comments WHERE post_id = 3; -- ... post_id = N まで繰り返し 記事が500件あれば、合計501回のクエリが発行されます。 ...

2026年2月10日 · 4 分 · 9989 文字 · uiuifree