N+1 問題とは何で、どう防ぎますか

一覧を 1 回取ったあと、1 件ずつ関連のデータを取りにいって、問い合わせが件数の分だけ増える問題。JOIN や IN でまとめて取る、ORM の先読み、まとめて取る仕組み (DataLoader) で防ぐ、と答えます。

答えてみる

面接官に「N+1 問題とは何で、どう防ぎますか」と聞かれたつもりで、答えを見る前に自分の言葉で答えます。

声に出して答えた人は「書かずに答えを見る」で開けます。

解説を読む

聞かれ方から関連するコースまで、この問いの解説を全部ここに置いています。

こう聞かれます

  • 「N+1 問題とは何で、どう防ぎますか」
  • 「一覧の画面が、件数が増えるほど遅くなります。何を疑いますか」
  • 「ORM を使っていると、なぜ N+1 に気づきにくいのですか」

面接官が見ている点

  • 何が N で何が 1 か、を具体的な例で説明できるか
  • JOIN、IN、ORM の先読み (eager loading)、DataLoader のような、まとめて取る手を挙げられるか
  • ORM はコードからは問い合わせの数が見えにくい、と分かっていて、記録で確かめる習慣があるか

短い答え

  • N+1 問題は、一覧を 1 回の問い合わせで取ったあと、その件数 N の分だけ 1 件ずつ関連のデータを取りにいき、問い合わせが N+1 回になる問題です。
  • JOIN や IN でまとめて取る、ORM の先読み (eager loading) を使う、GraphQL なら DataLoader のように同じ種類の取り出しをまとめる、で防ぎます。
  • ORM はコードの見た目から問い合わせの数が分かりにくいので、開発中に発行された SQL を記録に出し、件数を変えても回数が増えないかを確かめます。

少し詳しい答え

何が起きているか

「記事の一覧と、それぞれの書き手の名前」を出す画面を考えます。

SQL クエリ

-- 1 回目 記事を 100 件取る
SELECT id, title, author_id FROM posts LIMIT 100;

-- 2 回目から 記事ごとに書き手を取る (これが 100 回)
SELECT name FROM authors WHERE id = 1;
SELECT name FROM authors WHERE id = 2;
-- 以下続く

1 件ずつは速くても、DB との行き来が件数の分だけ増えるので、件数が増えるほど遅くなります。手元の少ないデータでは気づかず、本番でデータが増えてから遅くなる、というのがよくある形です。

まとめて取る

JOIN で 1 回にまとめるか、IN で 2 回にまとめます。

SQL クエリ

-- JOIN で 1 回
SELECT p.id, p.title, a.name
FROM posts p
JOIN authors a ON a.id = p.author_id
LIMIT 100;

-- IN で 2 回 (記事を取ったあと、書き手をまとめて取る)
SELECT id, name FROM authors WHERE id IN (1, 2, 3);

ORM (表の行をプログラムの物として扱う道具) では、関連を後から読み込む設定 (lazy loading) のままループの中で触ると、N+1 が起きます。先読み (eager loading、たとえば include や JOIN FETCH のような指定) で、関連もまとめて取るようにします。

GraphQL では、項目ごとに取り出しの関数が呼ばれるので N+1 が起きやすく、DataLoader のように、同じ瞬間に来た取り出しを 1 回の IN にまとめる仕組みを使います。

まとめて取ったあとに遅いなら、結ぶ列にインデックスがあるかも見ます (インデックスの話は別の問いで扱います)。

トレードオフ

まとめて取る方法にも弱点があります。

  • JOIN 1 対多の関連を JOIN すると、親の行が子の数だけ重なって返り、受け取る量が増えます。子が多いときは IN で 2 回に分けるほうが軽いこともあります
  • 先読みを何でも付ける使わない関連まで毎回取ると、かえって重くなります。その画面で使う関連だけ先読みします
  • IN に入れる数何万もの値を IN に並べると、DB によっては遅くなったり上限に当たったりします。数が多いときは分けて取ります

話すときは、自分が関わったサービスで一覧が遅くなった例を 1 つ用意し、直す前と後の問い合わせの回数と時間を話せるようにしておきます。

よくある間違い

  • 少ないデータで試して「速い」と言う N+1 は件数が増えてから効いてきます。件数を増やして回数を見ます
  • ORM の関連をループの中で触る 見た目は普通の取り出しでも、裏で 1 件ずつ問い合わせています
  • 何でも JOIN でまとめる 1 対多では行が重なって量が増えます。IN で分けるほうが軽いこともあります

深掘りの問い

  • 「N+1 を見つけるには、どうしますか」開発中に ORM が出す SQL を記録に出す、1 回の画面の表示で何回問い合わせたかを数える、遅い問い合わせの記録を見る、と答えます
  • 「API の作りで N+1 になることはありますか」 画面が一覧の API を呼んだあと、1 件ずつ詳しい API を呼ぶのも同じ形なので、一覧の応答に要る項目を入れる、まとめて取る API を用意する、と答えます
  • 「キャッシュで解決してよいですか」 回数は減りますが、古いデータが出る問題が増えます。まず取り方を直し、それでも重い所にキャッシュを足す、と答えます

確認日 2026年10月24日

技術面接問題の一覧へ戻る