0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

N+1クエリ問題

0
Posted at

はじめに

  • データ件数が増えた途端、急に遅くなった
  • 本番だけ重い

そんな経験はありませんか?

その原因として頻出するのがN+1クエリ問題です。

コードは綺麗。
例外も出ない。
しかし裏側では、大量のSQLが発行されている——
それがN+1クエリ問題の怖さです。

N+1クエリ問題とは

N+1クエリ問題とは、親データ取得後、ループ内で子データ取得のクエリが実行される問題です。

構造としては以下の形になります。

1件:親データ取得のクエリを実行

N件:親データの件数分ループして、子データ取得のクエリを実行

⇒合計 N+1 回のSQLが発行されます。

典型的な発生例

// ユーザー一覧を取得
var users = context.Users.ToList();

foreach (var user in users)
{
    // 各ユーザーの注文を取得
    var orders = context.Orders
        .Where(o => o.UserId == user.Id)
        .ToList();
}

発行されるSQL
Users 取得(1回) + Orders 取得(N回) = N+1回

なぜ問題なのか

処理時間の大半が計算ではなく、通信で消費されます。

通信回数が爆発する

  • SQL自体は軽い
  • しかし DBとの往復が大量に発生

データ量が増えると一気に悪化する

  • 開発環境:ユーザー100件 → 問題なし
  • 本番環境:ユーザー10万件 → 遅い

気付きにくい

  • コードはシンプル
  • エラーも出ない
  • SQLログを見るまで分からない

対策①:JOINで一括取得する

var result = context.Users
    .Join(
        context.Orders,
        user => user.Id,
        order => order.UserId,
        (user, order) => new
        {
            UserName = user.Name,
            OrderId = order.Id
        }
    )
    .ToList();

ポイント

  • SQLは 1回
  • DBに「まとめて取る」判断を任せる

対策②:IN句で一括取得する

// ユーザーID一覧を取得
var userIds = context.Users
    .Select(u => u.Id)
    .ToList();

// 注文を一括取得
var orders = context.Orders
    .Where(o => userIds.Contains(o.UserId))
    .ToList();

// メモリ上で紐付け
foreach (var userId in userIds)
{
    var userOrders = orders
        .Where(o => o.UserId == userId)
        .ToList();
}

ポイント

  • SQL発行は 2回
  • ループ内でSQLを発行しない

JOIN か IN か

JOIN は速い、IN は遅いと言われがちですが、そんなことはありません。

  • DB、インデックス、データ件数によっては IN の方が速い場合もあります
  • IN句はDBが最適化できます

重要なのは「どこで処理すべきか」を考えることです。

  • DBが得意なこと → DBに任せる(JOIN)
  • アプリが得意なこと → アプリで組み立てる(IN)

判断に迷ったときのチェックリスト

  • 画面表示・一覧? → JOIN
  • ID一覧がすでにある? → IN
  • 集計・検索条件が多い? → JOIN
  • ロジックの流れを壊したくない? → IN

おわりに

ORMは非常に便利ですが、

  • 何回SQLが発行されているか
  • どのタイミングでDBにアクセスしているか

を意識しないと簡単にN+1が生まれます。

本記事がN+1クエリ問題の理解と防止に役立てば幸いです。

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?