select_related
객체가 정참조(FK를 갖고 있을 때) 이거나, 역참조하는 single object(OneToOne, ManyToOne) 일 때 사용된다.
carts = Cart.objects.filter(user=user).select_related('product', 'size', 'color')
prefetch_related
객체가 역참조(FK 를 갖고 있지 않을 때) 이거나, 정참조하는 multiple objects(ManyToMany, OneToMany) 일 때 사용된다.
carts = Cart.objects.filter(user=user).prefetch_related('product', 'size', 'color')
사용의 이유
select_related
와 prefetch_related
는 객체뿐만 아니라 해당 객체가 참조하는 데이터들을 미리 캐싱하여 db hit 를 줄이기 위해 사용된다.
아래는 테이블은 유저들의 장바구니 정보가 담긴 테이블이다.
위 테이블의 모델은 product, size, color 모델의 fk 값을 가지고 있다.
이때, user_id=14 가 가지고 있는 값들을 filter, select_related, prefetch_related 로 불러와보고, 어떤 방식으로 불러와지는지 LOGGING 을 사용하여 확인해보자.
settings.py/LOGGING
django 는 ORM 을 사용하기 때문에 내가 쓰는 명령어가 어떤 SQL 쿼리문인지 알기 어려울 수 있다. django project의 settings.py에 아래의 내용을 추가하면 server를 run할 때 또는 shell에서 코드를 작성했을 때 SQL의 쿼리문을 보여준다.
LOGGING = {
'disable_existing_loggers': False,
'version': 1,
'formatters': {
'verbose': {
'format': '{asctime} {levelname} {message}',
'style': '{'
},
},
'handlers': {
'console': {
'class' : 'logging.StreamHandler',
'formatter' : 'verbose',
'level' : 'DEBUG',
},
'file': {
'level' : 'DEBUG',
'class' : 'logging.FileHandler',
'formatter' : 'verbose',
'filename' : 'debug.log',
},
},
'loggers': {
'django.db.backends': {
'handlers' : ['console','file'],
'level' : 'DEBUG',
'propagate': False,
},
},
}
filter 사용 시, runserver
select_related 사용 시, runserver
prefetch_related 사용 시, runserver
한 눈에 봐도 성능의 차이를 확연하게 느낄 수 있다.
select_related 와 prefetch_related 의 차이
select_related
와 달리, prefetch_related
는 JOIN을 하지 않고 개별 쿼리를 실행 후, django에서 직접 데이터를 조합하는 과정을 거친다. 즉, 두 번의 쿼리를 수행하는 것이다.
Cart.objects.select_related('product')
# query 가 Inner join 으로 끝난다.
Cart.objects.prefetch_related('product')
# query 가 cart 조회 후 product 조회의 2단계로 수행된다.
그렇다면 어떤 방법을 사용하는 것이 효율적일까?
두 api 의 효율성은 쿼리셋 조건과 데이터의 양에 따라 달라진다. db에 한번만 접근하는 select_related
가 db hit 를 줄여주어 보다 더 효율적으로 보이지만, 복잡하고 데이터 양이 많은 경우에는 한 번에 쿼리를 조회하는 것보다 두 번에 나누어 각각 조회할 때 응답속도가 빠를 수 있다. 따라서 상황마다 두 api 의 성능 및 결과를 비교해야 한다.
'django' 카테고리의 다른 글
QuerySet API (0) | 2021.04.27 |
---|---|
django (0) | 2021.04.27 |