프로젝트 경험기/MSA 경험기

[MSA 경험기] 모아 구독 (5) Query Profiling을 통한 최적화

yubi5050 2022. 11. 25. 06:27

지난 글

이전 글 에서는 조회수 구현시 동일 유저의 반복 접근 이슈를 쿠키를 활용하여 해결 방법에 대해 작성하였다.

https://yubi5050.tistory.com/222

 

[MSA 경험기] 모아 구독 (4) 조회수 구현 2 (by. 쿠키와 동시성)

지난 글 이전 글 에서 사실 조회수 구현에 대해 쿠키와 동시성을 같이 설명하려고 했으나, 동시성을 설명하는 과정이 길어져, 두 편에 나눠서 쓰게 되었다. (사실 조회수 기능을 구현했던 시간

yubi5050.tistory.com

 

이번 글에서는 프로젝트를 진행하면서 Query Profiling을 통해 수행 시간을 줄인 경험에 대해 작성해보려고 한다.

 

Query Profiling 도구 - Django debug toolbar

Profiling Tool로는 일전에 간단하게 사용해본 경험이 있는 Django-debug-toolbar를 사용하였다.

 

Debug Toolbar는 API 를 호출했을 때 세부 Query와 Query Time을 확인 할 수 있고, 추가로 Cache나 Logging 등도 확인 할 수 있다.

https://yubi5050.tistory.com/139

 

[Django] Query Profiling Tool (1) - Django debug toolbar

django-debug-toolbar 설치 pip install django-debug-toolbar settings.py INSTALLED_APPS = [ # ... 'django.contrib.staticfiles', 'debug_toolbar', ] MIDDLEWARE = [ # ... 'debug_toolbar.middleware.DebugToolbarMiddleware', # ... ] # DRF의 Response 로 return

yubi5050.tistory.com

 

Query 최적화 문제 - Lazy Loading, N+1 Problem

Django는 기본적으로 Lazy Loading 방식이라, DB 값에 대해 실제 필요할 때 로딩이 되고, 그러다보니 Query를 호출하는 과정에서 N+1 Problem 문제가 발생하게 된다.

 

📌 N+1 Problem 이란?

테이블(A) 조회시 해당 테이블(A)의 갯수(N)만큼 연관 관계에 있는 테이블(B)의 조회 쿼리가 호출되는 현상

ex) 테이블(A) 전체 조회 1 + 테이블 (B) N번 호출 => N+1

 

'모아구독' 서비스도 구독 상품 Table인 'Product' 와 관계를 가진 테이블들이 존재하여 해당 필드 위주로 최적화를 진행하였다.

  • 외래키 관계 : Payment Term(결제주기), Category, User 테이블
  • 외부에서 참조 : ProductImages(상품 상세 이미지) 테이블

 

Query 최적화 해결 방법 - Select_related, Prefetch_related

Django의 대표적인 최적화 방법인 select_related, prefetch_related는 해당 테이블과 관계된 table을 미리 로딩 (EagerLoading) 하여 caching 해주는 역할 수행한다. 해당 함수를 실행하면, 최초 Query문이 다소 복잡하게 호출되긴 하지만 모두 cache에 남게 되어 DB에 매번 다시 접근할 필요가 없음

 

select_related :  1:1, 1:M, 정참조 에 주로 사용 / Inner join

prefetch_related : M:N이나 M:1, 역참조에 주로 사용 / 각 table 별로 query

 

다음 아래 링크는 QuerySet을 최적화하는 기타 다양한 방법을 일전에 정리한 부분이다

https://yubi5050.tistory.com/136

 

[Django] Queryset 최적화 기법 (select, prefetch, F, Indexing 등)

사전 학습 내용 https://yubi5050.tistory.com/60 // ORM 이란? https://yubi5050.tistory.com/83 // ORM Queryset API https://yubi5050.tistory.com/130 // ORM Queryset 특징 QuerySet 최적화 기법 (1) select_related & prefetch_related 일반적으로

yubi5050.tistory.com

 

 

Query 최적화 사례  2가지 (1) 홈 화면 상품 조회 API

📌 API 설명

카테고리 리스트 / 인기 상품 리스트 / 최근 상품 리스트를 반환하는 API

 

📌 최적화 방법

  • 카테고리 리스트는 Cache로 대응
  • 인기 상품, 최근 상품 리스트는 PaymentTerm(결제주기) 테이블과 정방향 참조이기에, Select_related 기법을 사용

  

📌 최적화 로직 적용 전 결과

10개의 Product를 조회시 각 Product가 반복적으로 PaymentTerm 테이블을 조회하고 총 34초가 소요

 

 

📌 최적화 로직 적용 후 결과

Product에 대한 PaymentTerm(결제주기)의 LEFT OUTER JOIN하여 Caching되고, 반복적으로 조회하지 않게 됨

EagerLoading을 통해 Query 수가 12개 => 2개로 줄어들었고, 시간도 34초 => 18초로 감소