django에서 Queryset은 1개의 메인 쿼리와 0~N개의 서브 쿼리들로 구성되어 있다.
django.db.models.query.py
class QuerySet:
"""Represent a lazy database lookup for a set of objects."""
def __init__(self, model=None, query=None, using=None, hints=None):
self.model = model
self._db = using
self._hints = hints or {}
self._query = query or sql.Query(self.model)
self._result_cache = None
self._sticky_filter = False
self._for_write = False
self._prefetch_related_lookups = ()
self._prefetch_done = False
self._known_related_objects = {} # {rel_field: {pk: rel_obj}}
self._iterable_class = ModelIterable
self._fields = None
self._defer_next_filter = False
self._deferred_filter = None
@property
def query(self):
if self._deferred_filter:
negate, args, kwargs = self._deferred_filter
self._filter_or_exclude_inplace(negate, args, kwargs)
self._deferred_filter = None
return self._query
요약
어렵게 생각하지 않기로 했다. 그대로 받아들여보자
select_related, prefetch_related 는 하나의 쿼리셋을 가져올 때 연관되어 있는 객체들을 미리 불러오는 함수이다. 이렇게 불러온 데이터들은 result_cache에 캐싱되기 떄문에 중복 호출을 방지할수 있게된다.
두 함수는 DB에 액세스하는 횟수를 줄여 성능을 향상 시킬 수 있다.
query : 1개의 메인 쿼리로 부를 수 있다.
result_cache : SQL의 수행 결과가 저장되는 부분이다(캐싱)
저장된 데이터가 없을 경우 새로운 SQL문을 호출한다.
prefetch_related_lookups : prefetch_relate() 부분에 선언된 값들을 저장함, 추가 쿼리셋이라고 부른다.
각 쿼리셋은 DB에 접속할 때 캐시 메모리를 포함하고 있다. 처음 연산 시에 캐시가 비어있기 때문에 쿼리가 발생하고, 그 이후에 동일한 쿼리셋을 사용할 경우 추가적인 쿼리는 발생하지 않고 캐시에서 꺼내서 사용한다.
다른 호출 방식
select_related()
JOIN을 통해 데이터를 즉시 가져오는 방법 -> 추가 쿼리 발생 x
1:1 관계에서 사용할 수 있고, 1:N의 관계에서 N이 사용할 수 있다.
정방향 참조에서의 JOIN에서 유리하게 사용된다.
record = Record.objects.all()
for r in record:
r.category.name
이런식으로 평소에 출력하던걸
record = Post.objects.select_related('category).all()
for r in record:
r.category.name
이렇게 출력하면 후자는 미리 얽혀있는 관계에 대한 데이터를 이미 가져왔으므로 처리 속도 자체가 차원이 다르다.
prefetch_related()
추가적인 쿼리를 통해 데이터를 즉시 가져오는 방법 -> 추가 쿼리 발생
1:N의 관계에서 1이 사용할 수 있고, M:N 관게에서 사용할 수 있다.
역방향 참조에서 유리하게 사용된다.
context['contents'] = Content.objects.select_related('user').prefetch_related('image_set').filter(
user__id__in=lookup_user_ids
)
이 경우 content에 대한 쿼리 한번, prefetch_related에 작성한 필드에 대한 추가쿼리까지 총 두번 쿼리가 수행된다.
prefetch_related()의 필드에 N개의 값을 준다면, 각 필드에 대한 N개의 추가 쿼리가 발생하는 구조이다.
느낀점은 데이터가 어디까지 필요한지를 정확히 파악하고 함께 불러올지를 결정하는 것이 중요하다고 생각한다.
한번에 능숙하게 될 순 없겠지만 , 이 데이터들을 잘 다루는 것이 나중에 가서는 가장 중요할 것이라고 생각한다. 알고리즘 풀듯이 자주 공부해야할것같다.
Prefetch()
향후 업데이트
이해에 큰 도움을 준 블로그
링크텍스트