- Published on
N+1 Query 오류에 대응하는 방법
- Authors
- Name
- Orc Hwang
- @orchwang
TL;DR
Sentry Report 로 인지하게 된 오류이다. N+1 Query 는 ORM 사용 시 발생할 수 있는 상황으로 주 증상은 성능 저하 이다. 이 문제의 원인과 발생 과정을 살펴보고 솔루션을 찾아본다.
일반적인 발생 과정
- 부모, 자식 관계를 가지는 두개의 테이블이 존재한다고 가정해 보자. 두 테이블은 Foreign Key 로 연결되어 있을 것이다.
- 부모테이블 에서 복수의 데이터를 쿼리 하려 한다.
- 이때 각 행별로 자식테이블의 데이터를 추가로 가져오려 한다.
- 각 행별로 자식 테이블이 데이터를 별도 쿼리하는 경우 실행되는 쿼리의 수가 많아져 성능 저하를 유발한다.
Django 에서의 예시
Django 에서의 구현을 예로 들어 설명해 본다. 아래와 같이 두개의 테이블을 ORM Model 로 구현했다. Pizza model 이 부모, Topping model 이 자식 이다.
from django.db import models
class Topping(models.Model):
name = models.CharField(max_length=30)
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ForeignKey(Topping)
Serializer 를 아래와 같이 구성할 경우 Pizza 목록 1,000개를 가져오려 할때 Topping table 을 1,000번 쿼리하게 된다. 굉장한 비효율 이다.
class PizzaListSerialzer(serializers.Serializer):
email = serializers.CharField()
topping_name= serializers.CharField()
def get_topping_name(self, obj):
topping = Topping.objects.get(id=obj.topping)
return topping.name
우선 Serializer 에 연결할 QuerySet 은 아래와 같이 작성한다.
queryset = Pizza.objects.select_related(topping).all()
이후 Serializer 를 아래와 같이 구성하여 Topping 의 name 을 QuerySet 에 구성한다.
class PizzaListSerialzer(serializers.Serializer):
email = serializers.CharField()
topping_name= serializers.CharField()
def get_topping_name(self, obj):
return obj.topping.name