-
select_related 와 prefetch_relatedDjango 2022. 4. 2. 15:41
< select_related 와 prefetch_related 를 학습하기까지의 여정>
1. 1차 프로젝트를 통해 정참조, 역참조 개념 정리
2. 2차 프로젝트를 통해 데이터를 불러올 때의 편의성을 고려하는 것이 중요하단 것을 깨달음
- 처음 프로젝트를 진행하시는 분들은 무슨 말인지 모르실 수 있을 텐데 직접 그 불편함을 겪으신 후에 이에 대해
학습하고 개선하시는 게 좋은 경험이 될 것이기에 저처럼 모른 상태에서 시작하시는 것도 좋다고 생각합니다.ㅎㅎ
3. 기존 프로젝트들의 데이터가 워낙 적었고 기능 구현하기 바빠서 Query에 대한 고려를 하지 못 했었기에 이번에 select_related 와 prefetch_related 에 대해 학습하고 이를 이번 과제에서 사용할 목적으로 학습하려고 합니다.
아래는 기존의 프로젝트에서 작성하던 방식으로 했을 때의 Query log 입니다.
DB의 테이블도 7개밖에 없고 큰 틀의 데이터도 5개밖에 없는 정말 작은 데이터가 담긴 DB를 한번 전체 조회하는데 이렇게 많은 쿼리를 날렸다는 것을 알 수 있었습니다.
기존의 Query log 하지만 아래의 쿼리 로그를 보시면 굉장히 적어진 것을 보실 수 있습니다. 단지 select_related 와 prefetch_related를 적용했을 뿐인데 말이죠.
지금이야 데이터도 적고 유저도 저 한 명이니 문제가 없겠지만 위와 같은 방식으로 작성해둔 코드라면 조회하는 유저가 많아지고 데이터와 기능들이 많아지면 서버에 많은 부하가 갈 것이기에 selct_related 와 prefetch_related 를 적절하게 사용하는 것이 굉장히 중요합니다.
select_related 와 prefetch_related 적용 후의 Query log select_related 와 prefetch_related 는 무엇이고 왜 사용할까?
< 사용하는 이유 >
데이터 중복 호출을 방지하여 DB 접근을 최소화함으로써 부하를 줄이고 이를 통해 성능을 향상하기 위해서 사용
< 어떻게 DB 접근을 최소화하는가 >
불러온 데이터들을 캐쉬에 저장해 두고 거기에서 활용할 수 있는 데이터들은 DB에 접근하지 않고 캐쉬에서 가져다 씀
< select_related >
< 사용 범위 >
정참조에서 사용할 수 있기에 1:1 관계 및 1:N 관계의 N 에서 사용
< 특징 >
SQL 문법 중 Join을 실시하는 Django ORM이다.
한 번의 Query를 날려서 related objects 까지 불러온다.
서버가 종료되기 전까지 이런 데이터들을 캐쉬에 저장해 두고 사용한다.
< prefetch_related >
< 사용 범위 >
select_related + 역참조에서 사용
대체적으로 1:N의 1 그리고 다대다 관계의 역참조에서 사용
select_related보다 기본적으로 쿼리를 한번 더 날리기에 정참조에서는 굳이 사용할 필요가 없기에 역참조 관계에서 사용합니다.
< 특징 >
공식문서를 보면 select_related를 쓸 수 있는 관계에서도 사용이 가능하나
prefetch_related는 추가 쿼리를 발생시키기에 대체적으로 select_related를 사용할 수 없는 관계에서 사용하는 것이 좋다.
# models.py class Main_image(models.Model): image_url = models.CharField(max_length=1000) class Meta: db_table = 'main_images' class Movie(models.Model): main_image = models.OneToOneField('Main_image', on_delete=models.SET_NULL, null=True) genres = models.ManyToManyField('Genre') directors = models.ManyToManyField('Director') actors = models.ManyToManyField('Actor') class Meta: db_table = 'movies'
select_related를 사용하지 않는다면 전체 리스트를 조회할 때 만약 5개의 데이터가 있어서 for문을 돌린다면 5번의 쿼리를 날리게 됩니다. 만약 데이터가 1,000개라면..? 1번의 요청에 1,000개의 쿼리를 날리게 될 테고 이는 DB에 부하를 줄 것입니다.
그런데 이때 아래처럼 두 가지를 적절하게 사용한다면 정참조의 테이블에는 select_related로 1번, 역참조의 테이블에는 prefetch_related로 각 1번씩만 쿼리를 날려서 DB 부하를 줄일 수 있습니다.
그래서 아래의 경우에는 select_related 쿼리 1번, prefetch_related 쿼리 5번이기에 총 6개의 쿼리를 날리게 됩니다.
all_movie = Movie.objects\ .select_related('main_image')\ .prefetch_related('genres', 'directors', 'actors', 'sub_image_set', 'video_set')\ .all()
Q. 그러면 prefetch_related는 모든 관계에서 사용된다고 하는데 그러면 select_related를 안 쓰고 이것만 사용하면 되는 것 아닌가?
우선 둘의 가장 큰 차이점은 추가 쿼리의 발생 여부입니다.
그래서 위에서 설명한 것을 토대로 select_related 와 prefetch_related 를 적절하게 사용해야 합니다.
< 마무리 >
이번에 직접 사용해보고 Query log를 찍어보면서 그 효과를 조금이나마 실감할 수 있었습니다.
아직 내부 동작에 대해서는 이해가 부족하여 이 부분을 보충해야겠습니다.
< 참고 자료 >
Django 공식 문서
https://docs.djangoproject.com/en/4.0/ref/models/querysets/
김성렬 님의 영상인데 자세하고 쉽게 설명해 주셔서 꼭 보시길 바랍니다.
Django ORM (QuerySet)구조와 원리 그리고 최적화전략 - 김성렬 - PyCon Korea 2020
https://jupiny.tistory.com/entry/selectrelated%EC%99%80-prefetchrelated
'Django' 카테고리의 다른 글
카카오 소셜 로그인 API - update_or_create() (0) 2022.01.15 ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing (0) 2022.01.09 ManyToManyField 사용법 및 장점 (0) 2022.01.09 TypeError : Field 'id' expected a number but got <User : User object (1)> (0) 2022.01.05 Django 테이블 이름 변경 및 삭제 후 마이그레이션시 에러 (0) 2021.12.28