명심해야 할 점은 이른 최적화는 좋지 않다는 것이다. 아직 사이트의 규모가 크지않고 잘 로딩되고 있다면, 이 챕터는 넘어가도 좋다.
너무 많고 충분히 원활하지 않은 쿼리들로 인한 병목 현상을 어떻게 줄이는지 알아보자.
추가로 Django 공식 문서에서 데이터베이스 액세스 최적화에 대해 읽어 보길 바란다.
참조: https://docs.djangoproject.com/en/3.2/topics/db/optimization/
django-debug-toolbar를 사용하면 대부분의 쿼리가 발생하는 곳을 확인하는데 도움이 된다. 다음과 같은 병목 현상을 찾을 수 있을 것이다.
- 한 페이지 내에서 중복되는 쿼리
- 예상보다 더 많은 쿼리를 호출하는 ORM 호출
- Slow queries
아마 최적화가 필요한 URL이 대략적으로 떠오를 것이다. 딱 봐도 로딩이 느린 페이지 라던가..
아직 django-debug-toolbar가 없다면 로컬에 설치하라. 웹 브라우저에서 프로젝트를 보고 SQL 패널을 확장하고, 현재 페이지에 포함된 쿼리 수를 확인할 수 있다.
- ORM의 select_related() 함수를 활용하여 query들을 combine하기.
- select_related()를 사용할 수 없는 상태의 many-to-many, many-to-one 관계의 경우 prefetch_related()를 사용해보기.
- 동일한 쿼리가 템플릿당 두 번 이상 생성되는 경우 쿼리를 Python 보기로 이동하고 컨텍스트에 변수로 추가한 다음 템플릿 ORM 호출이 이 새 컨텍스트 변수를 가리키도록 하기.
- Memcached 또는 Redis와 같은 키/값 저장소를 사용하여 캐싱을 구현하고, 뷰에서 실행되는 쿼리 수를 확인하는 테스트를 작성한다.
- django.utils.function.cached_property 데코레이터를 사용하여 객체 인스턴스의 수명 동안 메서드 호출 결과를 메모리에 캐시해보기.
- 참조: cached_property
- 인덱스가 가장 일반적이고 느린 쿼리 속도를 높이는 데 도움이 되는지 확인하라. 해당 쿼리에 의해 생성된 원시 SQL을 살펴보고 가장 자주 필터링/정렬하는 필드에 대해 인덱싱을 하고, 생성된 WHERE 및 ORDER_BY 절을 살펴보라.
- 인덱스가 실제로 프로덕션에서 수행하는 작업을 이해하라. 개발 시스템은 프로덕션에서 발생하는 일을 완벽하게 복제할 수 없으므로 데이터베이스에서 실제로 발생하는 일을 분석하고 이해하는 방법을 배워야 한다.
- Common query의 쿼리 플랜을 살펴보라.
- 참조: query plan?
- 데이터베이스의 slow query 로깅 기능을 켜고 slow query가 자주 발생하는지 확인하라.
- 개발 중에 django-debug-toolbar를 사용하여 프로덕션에 들어가기 전에 잠재적으로 느린 쿼리를 방어적으로 식별하라.
좋은 인덱스가 있고 어떤 쿼리를 다시 작성해야 할지 알 수 있을 만큼 충분한 분석을 수행한 후에는 쿼리를 다시 작성하는 방법에 대한 몇 가지 시작 팁을 알아보자.
- 가능하면 더 작은 결과 집합을 반환하도록 로직을 다시 작성하라.
- 인덱스가 보다 효과적으로 작동할 수 있도록 데이터를 다시 모델링하라.
- ORM으로 생성된 쿼리보다 더 효율적으로 작동할 수 있는 곳에 원시 SQL로 작성하라.
Note
TIP: 분석 팁
MySQL의 EXPLAIN command 활용하기
django-debug-toolbar의 SQL panel에서도 EXPLAIN 기능을 사용할 수 있다.
대형 사이트의 관계형 데이터베이스에 절대 들어가지 말아야 할 두 가지
- Logs
- Ephemeral data (임시 데이터) : 지속적으로 다시 작성해야 하는 데이터는 관계형 데이터베이스에서 사용하기에 적합하지 않다. 대신 이 데이터를 Memcached, Redis 및 기타 비관계형 저장소로 이동하라.
- binary data
참조
고려할 사항
- 가장 많은 쿼리가 포함된 보기/템플릿은 무엇인가?
- 가장 많이 요청되는 URL은 무엇인가?
- 페이지의 캐시는 언제 무효화되어야 하는가?
Third-Party Caching Package가 제공하는 기능
- QuerySet 캐싱.
- 캐시 무효화 설정/메커니즘.
- 다양한 캐싱 백엔드.
- 캐싱에 대한 대체 또는 실험적 접근 방식.
주로 쓰이는 caching package
- django-cacheops
- django-cachalot