TIL96. select_related , prefetch_related

select_related와 prefetch_related란?

select_related와 prefetch_related는 하나의 Queryset을 가져올 때, 미리 related objects들까지 다 불러와주는 함수이다.
비록 query를 복잡하게 만들지만, 불러온 데이터는 모두 cache에 남아있게 되므로 DB에 다시 접근해야 하는 이슈가 줄어들 수 있다.
2가지는 모두 DB에 접근하는 수를 줄여준다.
일명 쿼리 다이어트!!

select_related와 prefetch_related의 차이

select_related

  • 정참조(M:1) 혹은 1:1 관계에서 사용이 가능하다.
  • 데이터가 많든 적든 JOIN을 통해 한 번의 쿼리문으로 모든 것을 가져온다.
  • 쿼리를 한번 호출 하는 것은 효율적이지만 데이터가 많을 때는 응답속도가 느려질 수 있다.

prefetch_related

  • 다대다(M:M), 역참조일 때 사용이 가능하나 one-to-one, Foreign key 등 모든 relationship에서도 사용 가능하다.
  • JOIN을 하지 않고 개별 쿼리를 실행한 후, django에서 직접 데이터 조합을 합니다.(쿼리 2회이상 실시)
Dog.objects.all()		     # Dog테이블 1번, Dog를 소유하고 있는 User마다 DB 방문 // 쿼리 폭탄😭
Dog.objects.select_related('User')   # inner join쿼리 수행 // 쿼리 1번 수행
Dog.objects.prefetch_related('User') # Dog조회, User조회 // 쿼리 2번 수행

prefetch_related(),select_related() 괄호안에 들어가는건 ()만 사용할거야! 라고 해석하면 쉬울 듯 하다.
사용할 테이블만 갖고오거나 join하기!!

>>> Notification.objects.prefetch_related('company').filter(id=2)
<QuerySet [<Notification: Notification object (2)>]>     -- 정참조


>>> Notification.objects.prefetch_related('image_set').filter(id=2)
<QuerySet [<Notification: Notification object (2)>]>     -- 역참조 _set붙히기


>>> Notification.objects.prefetch_related('tag').filter(tag__id=2)
<QuerySet [<Notification: Notification object (2)>, <Notification: Notification object (7)>, <Notification: Notification object (12)>, <Notification: Notification object (17)>, <Notification: Notification object (22)>, <Notification: Notification object (27)>, <Notification: Notification object (32)>
--MTM 관계

쿼리 호출 횟수 체감 해보기


MyUser.objects.all()을 사용하면 쿼리는 총 몇번 호출 될까?
정답은 총 4번 이다.

MyUser 모델의 data를 가져오는 쿼리 1개
MyUser 모델에서 가져온 3개의 data에 OneToOne으로 연결된 Company 모델을 가져오는 쿼리 3개

이번에는 MyUser.objects.select_related('company').all()를 사용하여 Company 모델들까지 모두 가져와보겠다!

이번에 쿼리 호출 횟수는 1번이다!

만약, MyUser에 100개의 company와 OneToOne 관계를 가지고 있는 100명의 User가 있다면
MyUser.objects.all()을 사용할 때 101번의 쿼리를 MyUser.objects.select_related('company').all() 로 단 1개의 쿼리로 모든 데이터를 가져올 수 있다.

동일한 조건의 DB에서 MyUser.objects.prefetch_related('company').all()로 쿼리셋을 가져와 보면

이번에는 2개의 쿼리가 실행되었다.

좋은 웹페이지 즐겨찾기