DotFriends

prefetch_related로 성능 향상하기

King of Silicon Valley 2021. 9. 5. 18:35
728x90

상품 리스트를 보여주는 ProductsView를 만들었다. 

class ProductsView(View):
    @input_validator
    def get(self, request):
        option = request.GET.get('option', None)
        offset = int(request.GET.get('offset', 0))
        limit  = int(request.GET.get('limit', 0))
        order  = request.GET.get('order', 'id')
    
        q = Q()
        if option == 'new':
            q = Q(is_new=True)
        
        if option == 'sale':
            q = Q(~Q(discount_percent=0))
        
        products = Product.objects.filter(q).order_by(order)[offset:offset+limit]
        
        results = [{
            'id'    : product.id,
            'name'  : product.name,
            'price' : int(product.price),
            'images':[image.url for image in product.image_set.all()]
        }for product in products]

        return JsonResponse({'results': results}, status=200)

간단하게 로직을 설명해보자면 

 

쿼리 파라미터로 옵션을 신상여부와 세일여부를 받을 수 있고 해당하는 조건에 맞는 상품리스트를 Json파일로 넘겨주게 된다. 

 

상품테이블은 이미지 테이블을 따로 두어서  이미지 테이블이 상품을 '참조' 하게 만들었다. 

 

그래서 상품에 해당하는 이미지들을 조회하려면 역참조를 해야한다. 

 

위와 같이 했을 때 데이터베이스에 몇번 쿼리가 날라가는지 살펴보자. 

 

포스트맨으로 요청을 보냈다. 

다른 옵션은 주지 않고 상품중에서 10개의 상품만 조회하는 요청을 보냈다. 

 

prefetch_related미사용 쿼리 

충격적인 결과다.  ㄴㅇㄱ

 

상품 10개의 데이터를 조회하는데 이미지 테이블에 각각 한번씩 쿼리를 날려서 총 11번의 쿼리가 날라가게된다. 

 

정말 비효율적이다. 

 

이제 prefetch_related를 사용해서 성능향상을 시켜보자 

 

products = Product.objects.filter(q).prefetch_related('image_set').order_by(order)[offset:offset+limit]

이제 다시 쿼리를 확인해보자

 

prefetch_related사용 쿼리 

2번으로 쿼리가 줄었다!!

 

SQL쿼리문을 살펴보면 image테이블에 10개를 한번에 조회하는 것을 알 수 있다. 

 

그렇다 prefetch_related를 사용하면 데이터를 한번에 조회하고 캐싱해두었다가 필요할 때 쓰는 것이다. 

 

또 데이터를 역참조할 때 사용한다.