QuerySet & Caching
- QuerySet은 ORM에서 django model objects를 통해 반환되는 객체이다.
- 각각의 쿼리셋은 DB에 접근할 때 캐시 메모리를 포함하고 있다. 처음 쿼리셋이 연산될 때, 캐시가 비어있기 때문에 query가 발생한다. 그 이후 동일한 쿼리셋을 사용할 경우 추가적인 쿼리는 발생하지 않고 캐시에서 꺼내어 사용하게 된다.
Query란?
- 데이터베이스에 정보를 요청하는 것
- 파일의 내용등을 알기 위해 몇개의 코드나 키를 기초로 질의하는 것을 의미하며 데이터베이스에 존재하는 자료를 사용자가 원하는 조건을 통해 검색하고 검색된 결과를 자유롭게 조회할 수 있는 기능 등을 지원한다.
ORM, QuerySet
- ORM은 데이터 베이스와 객체지향 프로그래밍 언어 간의 호한되지 않는 데이터를 변환하는 프로그래밍 기법이다.
QuerySet
- query 란 데이터베이스에 정보를 요청해주는 것을 의미하며 파이썬으로 작성한 코드가 sql 로 매핑되어 queryset 이라는 자료 형태로 값이 넘어오게 된다.
- 순회 가능한 데이터로서 이를 이용하여 1개 이상의 데이터를 불러와 사용할 수 있으며, queryset 의 특이한 점은 Lazy 한 특성이다.
- 미리 db 에 접근해서 값을 불러오지 않고, 출력 등과 같이 필요한 순간에 sql로 매핑되고 이를 통해 db에 접근해 값을 가져오게 된다.
- ORM 에서는 하나의 모델을 바탕으로 foreignkey가 묶인 다른 자료들도 불러서 사용할 수 있다. 하나의 모델을 불러올 떄 미리 foreignkey로 연결 된 자료까지 불러올 수 있으면 DB에 접근을 한번만 해도 된다.
- SQL에서 JOIN 구문은 하나의 쿼리에서 여러개의 테이블에 접근해서 다양한 값을 얻는데 select_related 와 prefetch_related도 마찬가지로 내부적으로 JOIN구문을 사용한다.
- 하나의 QuerySet을 가져올 때 미리 related objects들까지 다 불러와주는 함수
- 불러온 data들은 모두 cache에 남아있게 되므로 DB에 다시 접근해야 하는 수고를 덜어준다.
- SQL의 JOIN을 사용하는 특성상 foreign key, one-to-one과 같은 single valued relationship에서만 사용 가능하다.(1:1관계 혹은 1:N 관계에서 N이 사용 가능)
- 캐싱이 되기 때문에 모델들을 코드에서 재사용 할 경우 추가적이 쿼리가 필요하지 않다.
- select related는 inner join으로 데이터를 가져오기 때문에 쿼리를 1개만 사용한다.
# 1번 코드
car = Car.objects.get(id=1)
owner = car.owner
# 2번 코드
car = Car.objects.select_related('owner').get(id=1)
owner = car.owner
- 1번 코드의 쿼리 개수는 총 2개고, 2번 코드의 쿼리 개수는 총 1개다.
- 1번 코드는 Car 모델에서 car 를 id 로 get 하기 위한 쿼리 1개 + 그 car 의 owner 를 찾기 위한 쿼리 1개 해서 총 2개다.
- 그에 비해 2번 코드는 Car 모델에서 car 를 id 로 get 함과 동시에 Person 모델에서 car 의 owner 를 찾아둔다.
- ManyToManyField, ForeignKey의 reverse relation에서 활용(M:N 혹은 1:N 관게에서 1이 사용할 수 있다.)
- 각 관계별로 DB쿼리를 수행하고 파이썬 단에서 조인을 수행한다.
owner = Person.objects.prefetch_related('car_set').get(id=1)
for car in owner.car_set.all():
print(car.name)
- owner 라는 Person 인스턴스 하나가 가진 car 들의 이름을 전부 출력해주는 간단한 코드이다.
- prefetch related는 원래의 main query가 실행된 후 별도의 qery를 따로 실행하게 된다. 반면에 select related는 하나의 query만으로 related objects들을 다 가져온다.
- 완벽히 동일한 결과라도 prefetch related를 쓰느냐 select related 쓰느냐에 따라 query의 수가 달라진다.
Example
Pet.objects.prefetch_related('person__language_set')
- 위의 코드는 prefetch_related만 사용하였고, 결과적으로 3개의 query가 순차적으로 실행하게 된다.
- Pet의 모든 instance를 가져오기 위한 query
- 그 Pet instace들의 person을 가져오기 위한 query
- 그 person들의 language_set을 가져오기 위한 query
- select_related 를 적절히 사용하면 query 수를 더 줄일 수 있다.
Pet.objects.select_related('person').prefetch_related('person__language_set')
- Pet의 모든 instance와 그 intance의 person을 가져오는 query
- 그 person들의 language_set을 가져오기 위한 query
- many-to-many , many-to-one 과 같은 relationships에서는 어쩔 수 없이 prefetch_related 를 사용하여야겠지만, foreign-key , one-to-one 와 같은 single-valued relationships이 있는 곳에서는 최대한 select_related 를 사용하여 query 수를 줄여주는 것이 효과적이다.