N+1 문제는 데이터베이스에서 연관 데이터(Join)를 조회할 때 비효율적인 쿼리 방식으로 인해 발생하는 성능 문제이다.
from beanie import Document, Link
class Author(Document):
name: str
class Book(Document):
title: str
author: Link[Author]
# N+1 문제 발생
books = await Book.find()
for book in books:
author = await book.author.fetch()
print(f"{book.title} by {author.name}")
Book.find()에서 책을 조회하는 쿼리 1개 발생for book in books에서 각 책마다 author.fetch() 실행하여 3개(book의 개수)의 추가 쿼리 발생# fetch_links=True를 사용하여 author를 한 번에 로딩
books = await Book.find(fetch_links=True)
for book in books:
print(f"{book.title} by {book.author.name}")
# `SELECT * FROM books JOIN authors` 한 개의 쿼리만 발생
fetch_links=True를 사용하면 한 번의 쿼리로 연관된 데이터를 모두 로딩한다.aggregate 사용 가능# MongoDB Aggregation Pipeline
books_with_authors = await Book.find().aggregate([
{"$lookup": {
"from": "authors",
"localField": "author",
"foreignField": "_id",
"as": "author_info"
}}
]).to_list()
$lookup: MongoDB의 조인 기능을 사용하여 한 번의 쿼리로 데이터를 가져온다.$in 사용)# 여러 저자를 한 번에 가져오기
author_ids = [book.author.id for book in books]
authors = await Author.find({"_id": {"$in": author_ids}}).to_list()