카테고리 없음

Django ORM Lazy Loading, Eager Loading

khw7876 2022. 8. 8. 22:14

Django ORM에서 쿼리가 얼마나 찍히는지 

 

※ Lazy Loading(지연로딩)

Django ORM의 첫번째 특징 : 정말 필요한 시점에 SQL문을 호출한다.

users: QuerySet = User.objects.all()
orders: QuerySet = Order.objects.all()

이러한 쿼리셋들은 Lazy Loading에 속하는데, 쿼리셋을 선언하면 선언 시점에는 쿼리셋에 지나지 않는다.

실제로 이 쿼리문이 유저리스트가 되는 시점은 쿼리셋을 리스트로 묶어주었을 때 이다.

list(User.objects.all())

이렇게 list()로 묶어야만 그 시점에 SQL이 실제로 수행되고 데이터를 가져오게 되는 것이다.

- 따라서 첫번째 쿼리셋은 list()로 묶지 않았기에 SQL이 호출되지 않는다.

 

Django ORM의 두번째 특징 : 정말 필요한 만큼만 호출한다.

users: QuerySet = User.objects.all()

first_user: User = users[0]

user_list: list(User) = list(users)

해당 로직에서는 첫번째 유저를 얻기 위하여 ORM에서 SQL문을 호출 하였다.

하지만, 그 뒤에 users를 lost로 묶으면, 모든 user를 얻기 위하여 다시한번 SQL을 호출한다.

(쿼리수의 증가로 이어짐)

 

※ Caching (ORM이 필요한 시점에 필요한 데이터만 사용하기에 캐싱된 쿼리셋을 재사용 하는 방법)

위의 로직을 순서로 바꾸어서 사용을 해보자.

users: QuerySet = User.objects.all()

user_list: list(User) = list(users)

first_user: User = users[0]

이렇게 된다면 모든 user의 정보를 먼저 불러온다.

그리고 그 정보(캐시)에서 첫번째 유저를 가져온다.

(이미 불러온 정보에서 재사용 = 쿼리 추가 x)

 

※ Eager Loading(즉시로딩)

쿼리셋은 기본적으로 Lazy Loading을 사용하지만, 가끔 한번에 많은 SQL문 데이터를 가져와야 할 떄가 있다.

그 방법을 Eager Loading이라고 한다.

users: QuerySet = User.objects.all()  

for user in users:
    user.userinfo

이러한 로직이 있다고 가정하자.

  • users 테이블과 userinfo 테이블이 1:1 관계인 상황이다.
  • QuerySet의 기본은 Lazy loading이기에 for문이 반복되는 수만큼 계속 SQL문 호출
  • for문의 깊이만큼 쿼리수 증가

 

※ Eager Loading의 대표적인 두가지 Select_related() 와 prefetch_related()

  • Select_related
    • 정참조 방향에서 사용을 한다.
    • join을 통하여 데이터를 즉시 로딩하는 방식을 사용
users: QuerySet = User.objects.select_related("userinfo").all()  

for user in users:
    user.userinfo

이렇게 사용을 해주면 QuerySet을 불러올때 즉시 userinfo에 대한 정보까지 불러온다.

즉 users라는 Query_set에 이미 userinfo가 있기에 추가 쿼리수를 먹지 않는다.

 

  • Prefetch_related
    • 역참조 방향에서 사용을 한다.
    • 새로운 쿼리셋을 추가하는 방식이기에 선언한 prefetch_related의 개수만큼 쿼리가 추가적으로 호출
    • 역시 데이터를 즉시 로딩하는 방식을 사용

userlevel를 1대1로 역방향 참고한다고 가정하자.

users: QuerySet = User.objects.prefetcch_related("userlevel_set").all()  

for user in users:
    user.userlever_set

이렇게 사용을 해주면 역시 QuertSet을 불러올 때 userlevel에 대한 정보까지 불러온다.

user의 기준에서 userlevel은 역참조이기에 _set을 붙여 주도록 한다.

여기서는 prefetch_related를 하나만 사용 하였기에 사용되는 총 쿼리수는 2가지 이다!!