각 쿼리셋은 DB접근을 최소화하기 위한 캐시를 가지고 있다. 이를 이해하면 좀 더 효율적인 코드를 쓸 수 있다.
새롭게 만들어진 쿼리셋에서 캐시는 비어있다. 쿼리셋이 실제로 실행됐을때(hit the DB, SQL happens) 장고는 쿼리셋의 캐시에 그 결과를 저장하고 결과를 리턴한다.
쿼리셋 캐싱을 이용하지 않으면 비효율적인 코드를 만들 수 있기 때문에 캐싱에 대해 숙지해야 한다. 캐시를 사용하려면 쿼리셋 변수를 만들고 재사용하면 된다.
쿼리셋 변수를 만들지 않고 반복문 돌림
print([e.headline for e in Entry.objects.all()])
print([e.pub_date for e in Entry.objects.all()])
위 예시에선 쿼리셋 변수를 만들지 않았기 때문에 캐싱되지 않았고 재활용되지 않는다. 쿼리가 두 번 실행되므로 DB에 두번 접급하게 되기 때문에 캐싱을 이용하는 것보다 느리다. 게다가, 첫번째 코드가 실행됐으므로 이것이 두번째 코드에 영향을 줘서 서로 다른 데이터를 출력할 수도 있다.
이러한 문제를 피하려면, 쿼리셋을 저장해놓고 재사용하는 것 즉 캐싱을 활용할 수 있다
쿼리셋 변수를 만들고 반복문 돌림
queryset = Entry.objects.all()
print([p.headline for p in queryset]) # Evaluate the query set.
print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.
위 예시에선 쿼리셋 변수를 만들었기 때문에, 쿼리가 한번만 실행됐고 두번째 반복문 이후로도 캐싱된 것이 재활용 된다.
장고 공식문서 번역:
When QuerySets are not cached
Querysets do not always cache their results. When evaluating only part of the queryset, the cache is checked, but if it is not populated then the items returned by the subsequent query are not cached. Specifically, this means that limiting the queryset using an array slice or an index will not populate the cache.
For example, repeatedly getting a certain index in a queryset object will query the database each time
쿼리셋이 항상 결과를 캐시하는 것은 아닙니다. 쿼리 집합의 일부만 평가할 때 캐시가 검사되지만 채워지지 않은 경우 후속 쿼리로 반환된 항목은 캐시되지 않습니다. 특히 배열 슬라이스 또는 인덱스를 사용하여 쿼리 집합을 제한하면 캐시가 채워지지 않습니다.
예를 들어 쿼리 집합 객체에서 특정 색인을 반복적으로 가져 오면 매번 데이터베이스가 쿼리됩니다.
queryset = Entry.objects.all() print(queryset[5]) # 쿼리문이 실행 print(queryset[5]) # 또 쿼리문이 실행됨
그러나 전체 쿼리셋이 평가되면(evaluated) 캐싱 됩니다.
queryset = Entry.objects.all() [entry for entry in queryset] # 쿼리문이 실행되고 캐싱됨(반복문이 실행됐으므로 쿼리셋이 평가되었음) print(queryset[5]) # 캐싱 사용 print(queryset[5]) # 캐싱 사용
Here are some examples of other actions that will result in the entire queryset being evaluated and therefore populate the cache:
쿼리문이 실행되고 캐싱된 다른 예시:
[entry for entry in queryset] bool(queryset) entry in queryset list(queryset)
참고:
단순히 쿼리셋을 프린트하는걸로는 캐싱되지 않습니다. 왜냐하면__repr__()
를 호출하는 것(print)은 오직 전체 쿼리셋을 슬라이스한것을 반환하기 때문입니다.
관련자료
https://leffept.tistory.com/311
https://rimi0108.github.io/django/understand-queryset/