TL;DR

1 つのビジネストランザクションの中で、データベースの呼び出しが N+1 回発生してしまう、パフォーマンスのアンチパターン。一覧を 1 回取得したあと、その N 件それぞれについて関連データを取得すると起きる。

例

次のコードでは、全ユーザを取得する SQL が 1 回、各ユーザの投稿を取得する SQL が N 回呼ばれている。

const users = await getUsers()
for (const user of users) {
  const posts = await getPostsByUserId(user.id)
  // ...
}

解決するには、Eager Loading や JOIN を使うか、N 回のクエリを 1 回にまとめる。

const users = await getUsers()
const userIds = users.map((user) => user.id)
const posts = await getPostsByUserIds(userIds)

GraphQL では、Resolver がフィールドごとに実行されるので N+1 問題が起きやすい。一般には DataLoader を使って避ける(GraphQL における N+1 問題の発生しやすさはデメリットなのか)。

自分の理解

データベースの呼び出しに限らず、API の呼び出しなど、コストのかかる I/O でも同じ問題が起こりうる。

関連