Open4

N+1問題について📝

ピン留めされたアイテム
まさぴょんまさぴょん

N+1問題とは?

N+1問題(N+1クエリ問題)とは、主にデータベースアクセスやORマッパー(ORM)を用いた開発時に頻繁に発生する、パフォーマンス上の非効率性に関する問題のことを指します。

概要

典型的なN+1問題は、以下のようなパターンで生じます。

  1. 1回のクエリでN件のデータ(親データ)を取得する。
  2. 取得したN件の各レコードに対し、関連する別のテーブルや関連オブジェクトを取得するために追加の1件ずつのクエリが必要になる。

結果的に、

  • 最初の「N件を取得する1回のクエリ」
  • その後に「N件分の関連データを取得するN回のクエリ」

合わせて「N+1回」のクエリが実行されてしまう、という構造です。

具体例

例えばブログの記事と著者情報(多対一の関係)をORMで取得するケースを考えます。

  • クエリ1(N件を取得)SELECT * FROM posts;
    この結果として、N個の投稿が返ってきたとします。
  • クエリ2~クエリN+1(N回発行)SELECT * FROM authors WHERE id = ?;
    (各postごとに関連するauthorを1件ずつ取得)

このような場合、投稿数が多いほど著者情報取得のためのクエリが多発し、パフォーマンス低下を招きます。

N+1問題の影響

この問題は、データベースへのクエリ数が増加するため、以下のような影響を及ぼします:

  1. パフォーマンスの低下:
    • 不必要に多くのクエリが発行されることで、データベースの負荷が増えて、アプリケーションの応答速度が低下します。
  2. スケーラビリティの問題、サーバーダウンの可能性:
    • 大量のデータを扱う際に、N+1問題が顕著になると、システム全体のパフォーマンスが低下します。
    • また、メモリの使用率増加による、サーバーダウンの可能性などもあります。

対処方法

N+1問題への対処法としては、以下のような手段が挙げられます。

  • Eager Loading(事前読み込み)
    一度に関連テーブルもJOINしてまとめて取得することで、1回または少数のクエリで必要な情報を取り切る。

  • バッチフェッチング・子テーブルのまとめ読み
    ORMの機能を利用して、関連するIDのリストをまとめて1回のクエリで取得するような方法。

  • キャッシュの利用
    頻繁に参照するデータをメモリ内キャッシュやRedis等に保持して、DBへのクエリ回数を削減する。

まとめ

N+1問題は直感的なコード記述(例えばループ内で個別にクエリ発行)を行った際に容易に発生し、パフォーマンスを著しく損ないます。
ORMやフレームワークが提供するEager Loading、適切なクエリ構造への変更、キャッシングなどを活用することで、この問題は回避あるいは軽減することができます。

まさぴょんまさぴょん

N+1問題について📝

本質的な問題は "N" であることではないかと考えます。
増加し続ける可能性がある "N" という可能性そのものの問題を指している。

https://qiita.com/muroya2355/items/d4eecbe722a8ddb2568b

https://qiita.com/musenmai/items/e48e5594e6237a57703c

https://qiita.com/1129-tame/items/5c8db29e4718b9f38959

https://zenn.dev/goldsaya/articles/3af48dadc6cc0f

https://zenn.dev/kinzal/articles/c5745e7d9a950c