1. Lazy Loading
![ORM](https://velog.velcdn.com/images/chaduri7913/post/608bed8b-78a0-4318-817e-1ed6916dba20/image.png)
- 정의: 필요한 QuerySet 혹은 구문을 저장해놓고 DB 요청이 필요한 시점에만 요청을 보내는 것
- Query문 실행 횟수: 위와 같이 ORM을 실행했을 때 DB에 Query문은 0번 실행된다.
- DB에 요청을 보내는 시점: QuerySet Evaluation이 되는 시점은 Slicing, Iteration, repr(), len(), list(), bool() ... 등이다. 이때만 DB에 요청을 보냄
- 성능 저하: Lazy Loading에서는 성능 저하 이슈는 거의 없음
2. Caching
![](https://velog.velcdn.com/images/chaduri7913/post/d5f4e9f8-6996-4839-a9d8-a425b4937749/image.png)
- 정의: 메모리에 작업을 올려 놓는 것
- Query문 실행 횟수: 1번
- DB에 요청을 보내는 시점: list() 사용시
- 성능 저하: Caching을 하면 통신 효율성 증가
캐시 저장
캐시 저장 x
![](https://velog.velcdn.com/images/chaduri7913/post/3e959867-358a-4401-a8fc-820fa8d16dfa/image.png)
- _result_cache: 쿼리셋 캐시에 저장되어 있는 값을 불러오는 메서드
- queryset[0]으로 했을 때 쿼리셋 캐시에 저장하지 않는다.
캐시 저장 o
![](https://velog.velcdn.com/images/chaduri7913/post/c25f9134-9e41-4724-971c-a99b90f747f7/image.png)
- for문으로 사용할 때는 쿼리셋 평가를 하면서 캐시에 저장된다.
- 통신 효율을 줄일 수 있다.
3-1. N+1 Problems(정참조)
![](https://velog.velcdn.com/images/chaduri7913/post/586e90e5-39e1-4c57-af94-f1108b1b35ba/image.png)
![](https://velog.velcdn.com/images/chaduri7913/post/8cf93602-cefa-4d6c-9824-bd008fccd481/image.png)
-
정의: N+1 Problem이란 캐싱 데이터에 저장되어 있는 값이 없을 때 다시 DB에 직접 통신을 해서 계속 가져와야 한다.
-
book.pubilsher.name: book.publisher.name 처럼 한 테이블에서 다른 테이블을 참조를 할 경우 통신 효율성이 매우 떨어지게 된다.
-
latency가 발생하는 이유: for book in querset을 할 때 book에 대한 정보가 캐싱되지만, book.publisher.name을 불러올 때는 캐싱 데이터가 정보가 없어서 계속해서 DB에 요청을 해야하기 때문이다.
-
latency: 클라이언트 요청이 들어왔을 때 응답까지의 시간. 보통 0.4~0.7초에 반환되어야 좋은 API이다.
-
해결방법: 한 번에 데이터를 가져와서 캐시에 저장한다.
- Queryset = Book.obejects.all().select_related("publisher")
select_related()란 한 객체에서 다른 객체를 참조한 것을 합쳐서 객체를 만들어 주는 ORM이다.
3-2 N+1 Problems(역참조)
![](https://velog.velcdn.com/images/chaduri7913/post/0bcfbf45-3a44-4656-b51e-3541abd4b2c0/image.png)
![](https://velog.velcdn.com/images/chaduri7913/post/913515f2-98c6-43ff-bb7e-f97137796ee6/image.png)
- store.books.all()에서 데이터를 가져올 때 문제 발생
![](https://velog.velcdn.com/images/chaduri7913/post/a4decc2c-1cac-4ef4-9d2a-494c1239d59e/image.png)
- 해결 방법: prefetch_related()를 사용해서 역참조를 하는 테이블 합친다.
- 목적: 한 번에 가져와서 통신을 하자
- 쿼리 실행 횟수: 2번
- Store 1번
- books 1번
- 차이점:
- select_related(): sql단에서 join문을 써서 정참조 한 값을 더해서 테이블을 하나로 가져온다.
- prefetch_related(): sql단에서 역참조 한 값을 합쳐서 가져 오지 않고, 추가로 query를 가져와서 장고단에서 붙이는 것이다.
3-3 N+1 Problems(역참조)
![](https://velog.velcdn.com/images/chaduri7913/post/933cc8c7-eaab-499f-a653-5e5c353b57f8/image.png)
![](https://velog.velcdn.com/images/chaduri7913/post/ccd3cc28-4ecd-4650-bafa-0bc8f301a05a/image.png)
- 상황: prefetch_related()를 썼지만 filter() ORM을 사용함. 이럴 경우 캐시 데이터를 사용해서 DB와의 통신이 없을 것 같지만 filter 때문에 DB와의 통신이 발생한다.
![](https://velog.velcdn.com/images/chaduri7913/post/65298b52-a4a3-49dc-81b5-b7d10751623e/image.png)
- 해결방법: prefetch_related() 안에 Prefetch() 객체를 만든다. 그리고 그 안에 가져올 테이블의 이름, queryset, to_attr=이름 을 작성한다.
to_attr: queryset이 다른 조인 쿼리에도 사용된다면 해당 쿼리가 실행될 때마다 새로 조회를 하므로 중복조회가 발생됩니다.
이때, prefetch()에서 제공하는 to_attr을 사용하여 쿼리를 메모리에 저장하여 효율적으로 사용할 수 있습니다.
- 쿼리 실행 횟수: 3번
- prefetch_related: 1번
- Book.objects.all(): 1번
- filter(name='Book9991'): 1번