본문 바로가기

Django/성능 개선 + @

[Django] ORM 성능 개선하기 ( + debug toolbar )

 

 

Django를 사용하면서 ORM도 자주 사용해보고 queryset을 만들어서 API를 여러번 개발공부를 해왔지만, 해당 API를 호출할 때마다 요청시간이 길어진다는 것을 느끼게 되었고, 디버그 툴바를 이용해서 어떤 쿼리가 반복되는지를 확인해 보았다.

메가 쇼핑몰을 개발하던 도중 여러 모델과의 관계가 복잡해지지만 반대로 쿼리셋은 복잡해진 모델 관계와는 다르게 큰 변화가 없었다.
이렇게 개발해도 되나? 라는 의문이 들었고 성능에 대해서도 관심을 점차 갖게 되었다.

 

 

1. 기존의 문제점

메가 쇼핑몰 특성상 마켓관리자는 자신의 마켓에서 판매하는 상품들을 CRUD할 수 있어야 한다.

즉, Product 모델은 다양한 모델과의 관계를 형성하고 있다.

 

기존의 마켓 별 상품 리스트 API를 호출해보면,

12 queries 와 9 similar, 3 duplicates이 있는 것을 확인할 수 있다.

    def get_queryset(self):
        market_id = self.kwargs['market_id']

        return Product.objects.filter(market_id=market_id)

 

 

2. ORM 중복 쿼리 제거하고 성능 개선

중복된 쿼리를 해결해서 성능을 향상시키기 위해 ORM에서 select_related와 prefetch_related를 사용해서 중복된 쿼리를 없앨 것이다.

 

문제점 1 : product_like_user 컬럼 similar

product_like_user은 상품과 유저가 ManyTonyField로 구성되어 있기 때문에 prefetch_related를 사용해서 쿼리를 최적화 시켰다.

 

문제점 2 : Market의 Foreignkey의 관계를 가지는 Product와 User에 대한 duplicates

SQL JOIN을 사용하여 Market과 관계를 가지는 Product, User를 SELECT 문으로 포함시켜 DB쿼리를 한번에 가져오도록 해서 중복 문제를 해결하였다.

def get_queryset(self):
    market_id = self.kwargs['market_id']

    return Product.objects \
        .filter(market_id=market_id) \
        .select_related('market') \
        .select_related('category') \
        .prefetch_related('product_like_user') \
        .all()

기존의 쿼리는 9개의 similar와 3개의 duplicates로 좋지 않은 쿼리를 실행해 왔다면, 변경 후에 4개의 쿼리만 실행되도록 하여 성능을 확실히 개선되었다.

 

마켓별 상품 리스트 : 89ms -> 64ms

로 데이터가 적음에도 불구하고 쿼리 속도가 상당히 개선되었다는 것을 볼 수 있다.