prefetch

sihwan_e·2021년 6월 5일
0

Django

목록 보기
18/21

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()
향후 업데이트

이해에 큰 도움을 준 블로그
링크텍스트

profile
Sometimes you gotta run before you can walk.

0개의 댓글