Open4
N+1問題について📝
ピン留めされたアイテム
N+1問題とは?
N+1問題(N+1クエリ問題)とは、主にデータベースアクセスやORマッパー(ORM)を用いた開発時に頻繁に発生する、パフォーマンス上の非効率性に関する問題のことを指します。
概要
典型的なN+1問題は、以下のようなパターンで生じます。
- 1回のクエリでN件のデータ(親データ)を取得する。
↓ - 取得した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問題の影響
この問題は、データベースへのクエリ数が増加するため、以下のような影響を及ぼします:
- パフォーマンスの低下:
- 不必要に多くのクエリが発行されることで、データベースの負荷が増えて、アプリケーションの応答速度が低下します。
- スケーラビリティの問題、サーバーダウンの可能性:
- 大量のデータを扱う際に、N+1問題が顕著になると、システム全体のパフォーマンスが低下します。
- また、メモリの使用率増加による、サーバーダウンの可能性などもあります。
対処方法
N+1問題への対処法としては、以下のような手段が挙げられます。
-
Eager Loading(事前読み込み):
一度に関連テーブルもJOINしてまとめて取得することで、1回または少数のクエリで必要な情報を取り切る。 -
バッチフェッチング・子テーブルのまとめ読み:
ORMの機能を利用して、関連するIDのリストをまとめて1回のクエリで取得するような方法。 -
キャッシュの利用:
頻繁に参照するデータをメモリ内キャッシュやRedis等に保持して、DBへのクエリ回数を削減する。
まとめ
N+1問題は直感的なコード記述(例えばループ内で個別にクエリ発行)を行った際に容易に発生し、パフォーマンスを著しく損ないます。
ORMやフレームワークが提供するEager Loading、適切なクエリ構造への変更、キャッシングなどを活用することで、この問題は回避あるいは軽減することができます。
N+1問題について📝
本質的な問題は "N" であることではないかと考えます。
増加し続ける可能性がある "N" という可能性そのものの問題を指している。
TypeORM における N+1 問題を理解する
N+1問題の検出方法などについて📝
- Queryの発行ログを目視で確認する📝
- N+1問題を検出するライブラリなどを活用する📝