【えぬぷらすわんもんだい】

N+1問題 とは?

最終更新:
💡 一覧1回に、行ごとの追加取得が積み重なる

一覧を取得する1回のクエリに加え、N件それぞれの関連データを取得するクエリが出て、合計N+1回になる問題。ORMの関連データの必要時取得や、ループ内の個別取得などで起きる。

📌 このページのポイント
N=3の追加取得を比較 個別に取得 まとめて取得 4回 2回 アプリ DB アプリ DB 一覧3件 著者1 著者2 著者3 一覧3件 著者を一括 1 + 3 = 4回 1 + 1 = 2回
各記事で著者の追加SQLが1回ずつ出る場合と、3人の著者を一括取得する場合の例です。矢印はアプリからDBへの問い合わせを表します。
ひよこ ひよこ
N+1問題って具体的には?
ペンギン先生 ペンギン先生
記事3件の一覧と著者名を表示する例で考えよう。一覧を1回で取り、各記事の著者を別々のSQLで1回ずつ取得すると、1+3で4回になる。件数が増えるにつれて問い合わせも増え、通信や処理の負担が大きくなるんだ。
ひよこ ひよこ
ORMなら必ずこうなるの?
ペンギン先生 ペンギン先生
必ずではないよ。関連データを必要時に読む場合でも、すでに読み込み済みなら追加SQLが出ないことがある。実際に各件で追加取得が起きているか、SQLのログを見よう。ORMを使わず、ループ内で自分でSQLを書く場合にも起きるんだ。
ひよこ ひよこ
どう減らせばいいの?
ペンギン先生 ペンギン先生
画面に必要な関連データをまとめて読む方法があるよ。Railsで著者の事前取得を指定すれば、記事と著者を別々のクエリで取得する。この単純な関連の例では、一覧1回と著者をまとめる1回の計2回になる。図もこの方法との比較だよ。
ひよこ ひよこ
JOINすれば同じことになる?
ペンギン先生 ペンギン先生
必要な列を結合して一度に取得する方法もあるよ。ただし、SQLでJOINしただけでORMの関連オブジェクトが読み込み済みになるとは限らない。ORMの読み込み方法を確かめよう。1対多の結合では行が増えるので、重複や取得量にも注意するんだ。
ひよこ ひよこ
一括取得に直せば確認は終わり?
ペンギン先生 ペンギン先生
必要な関連が全部読み込まれているか、不要なデータまで取っていないかも見るよ。SQLの回数だけでなく、取得行数や実行時間も変更前後で比べよう。複数の関連をたどる画面では、それぞれで追加取得が残っていないか確認するんだ。
ペンギン
まとめ:ざっくりこれだけ覚えればOK!
「N+1問題」って出てきたら「一覧を取った後、各件の関連データを個別に取りに行く問題」と思えばだいたいOK!
📖 おまけ:英語の意味
「N+1 Problem」 = N+1回の問題
💬 一覧取得の1回に、N件分の追加取得が重なるのでN+1だよ。対策で常に1回へ減らせるという意味ではないんだ。

参考資料

← 用語集にもどる