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가지 이다!!