TIL - 04/25 - select_related, prefetch_related

Sung Jun Jin·2020년 4월 26일
0

TIL

목록 보기
14/25

현재 작업하고 있는 1차 프로젝트의 상품 model 일부분이다.

Product : 상품
Detail : 상품 상세설명
Media : 상품 이미지, 영상

각 클래스 관계 요약

  • Product one-to-one Detail
  • Media many-to-one Product

models.py 구성

class Product(models.Model):
	detail = models.ForeignKey("Detail", on_delete = models.SET_NULL, null = True)
    
    class Meta:
    	db_table = 'products'

class Detail(models.Model):
	summary  = models.CharField(max_length = 500)
   	class Meta:
    	db_table = 'details'

class Media(models.Model):
	media_url = models.URLField(max_length = 2000)
	product   = models.ForeignKey('Product', on_delete = models.SET_NULL, null = True)    

select_related는 QuerySet을 반환할 때 foreign-key, one-to-one 관계인 모델들을 함께 가져오는 ORM이다. DB 단에서 INNER JOIN으로 쿼리하여 데이터를 캐싱한다. 데이터를 캐싱(caching)한다는 것은 자주 사용되는 데이터를 메모리에 저장해 데이터가 필요할때마다 별도의 쿼리없이 메모리에서 가져오는 것을 의미한다. 정참조 관계에서 사용한다.

select_related를 실행했을때와 쿼리 실행 개수 비교

  • get
def get(self,request,product_code)
	product = Product.objects.get(code = product_code)
    response = product.detail.summary
    return JsonResponse({'data' : response}. status = 200)    
Function : get
Number of Queries : 4
Finished in : 0.03s
  • select_related.get
def get(self,request,product_code)
	product = Product.objects.select_related('detail').get(code = product_code)
    response = product.detail.summary
    return JsonResponse({'data' : response}. status = 200)   
Function : get
Number of Queries : 3
Finished in : 0.02s

쿼리의 개수가 1개 차이난다. 이유는 product.detail.summary 구문이다. select_related를 사용하지 않으면 정참조 관계인 detail 클래스가 메모리에 캐싱되어있지 않아서 위 구문을 사용할때 또 하나의 쿼리를 별도로 수행해야 하기 때문이다.

many-to-one 혹은 many-to-many의 역참조 관계에서 별도의 2개의 쿼리를 수행후 JOIN하여 데이터를 캐싱한다.

prefetch_related를 실행했을때와 쿼리 실행 개수 비교

  • get
def get(self,request,product_code):
	selected_product = Product.objects.get(code = product_code)
	response = [media.media_url] for media in selected_product.media_set.all()]
Function : get
Number of Queries : 4
Finished in : 0.02s
  • prefetch_related
def get(self,request,product_code):
	selected_product = Product.objects.prefetch_related('media_set').get(code = product_code)
	response = [media.media_url] for media in selected_product.media_set.all()]
Function : get
Number of Queries : 3
Finished in : 0.01s

추가적으로 발생한 쿼리

(0.001) SELECT `products`.`id`, `products`.`code`, `products`.`name`, `products`.`price`, `products`.`gender`, `products`.`color_name`, `products`.`silhouette_id`, `products`.`category_id`, `products`.`group_id`, `products`.`detail_id` FROM `products` WHERE `products`.`code` = '167592C' LIMIT 21; args=('167592C',)

간단한 예시이기 때문에 쿼리와 실행속도에서 미미한 차이를 보이지만 더 길고 복잡한 코드를 작성할수록 select_related와 prefetch_related를 사용해 쿼리의 개수를 줄이는것이 중요해질거 같다.

profile
주니어 개발쟈🤦‍♂️

0개의 댓글