택시짱의 개발 노트

24장. 장고 성능 향상시키기 본문

Django(장고)

24장. 장고 성능 향상시키기

택시짱 2021. 12. 8. 22:54

24.1 정말 (성능이란 것이) 신경 쓸 만한 일일까?

  • 서툰 최적화는 오히려 해가 될 수 있다. 사이트가 아직 종소규모이고 페이지 로딩 속도에 문제가 없다면 이 장을 건너 뛰어도 무방하다.

24.2 쿼리로 무거워진 페이지의 속도 개선

  • 느리게 반응하는 쿼리 때문에 생긴 문제를 해결하는 방안을 다룰 예정 ( 링크 참조 )

24.2.1 django-debug-toolbar를 이용하여 문제가 되는 쿼리 찾아내기

  • django-debug-toolbar를 이용하면 쿼리의 출처를 대부분 찾아낼 수 있다. 아래와 같은 병목구간 또한 발견할 수 있다.

    • 페이지에서 중복된 쿼리들
    • 예상한 것보다 많은 양의 쿼리를 호출하는 ORM 호출( call )
    • 느린 쿼리
  • 여러분의 장고 프로젝트에서 대강 어느 URL부터 해볼지 머릿속에 감이 올 것이다.

  • django-cache-panel는 장고에서 캐시의 이용을 시각화해 보여준다.

  • django-extensions는 장고의 프로파일링 도구를 활용해 run-server 명령을 시작하는 RunProfileServer라는 도구를 제공

  • silk는 사용자에게 인터페이스를 보여주기 이전에 HTTP 요청과 데이터베이스 쿼리를 낚아채 저장함으로써 실시간 프로파일링을 가능하게 해준다.

24.2.2 쿼리 수 줄이기

  • ORM에서 여러 쿼리를 조합하기 위해 select_related()를 이용해 본다. select_related는 FK 관계를 추적하여 좀더 많은 관계 정보를 담고 있는 큰 크기의 쿼리세트를 생성한다.
  • 다대다 대응(N:M)과 다대일 대응(N:1) 관계 에서는 select_related로 최적화 불가능. prefetch_related()를 이용
  • 템플릿당 동일한 쿼리가 두 번 이상 생성되는 경우 해당 쿼리를 파이썬 뷰로 이동시켜 context를 변수로 처리하고 context에서 템플릿 ORM이 호출될 수 있도록 하자.
  • Memcached or Redis와 같은 key/value 저장소 (NoSQL)을 사용하여 캐싱을 구현
  • django.utils.functional.cached_property decorator를 이용하여 객체 인스턴스의 메서드 호출 결과를 메모리에 캐시에 저장하여 사용

24.2.3 일반 쿼리 빠르게 하기

개별 쿼리 속도 또한 병목 지점이 되기도 한다.

  • 일반적으로 느린 쿼리들의 대부분은 인덱스로 최적화할 수 있다. 쿼리에서 가장 빈번히 필터되고 정렬되는 항목에 대해 인덱스를 설정한다. 생성된 SQL 문에서 WHERE 절과 ORDER_BY절을 세심하게 살펴보자
  • 실제 상용 환경에서 생성된 인덱스들이 정확히 어떤 역활을 하는지 살펴본다. 개발 머신의 환경이 상용 머신의 환경과 완벽히 일치되는 경우는 없다. 데이터베이스에서 어떤 일이 생기는지 이해하고 분석해 보자.
  • 쿼리에서 생성된 쿼리 계획(query plan)을 살펴 보자.
  • 데이터베이스에서 느린 쿼리 로깅(show query logging) 기능을 활성화하고 빈번히 발생하는 느린 쿼리를 확인하자.
  • django-debug-toolbar를 이용하여 잠재적으로 느려질 쿼리를 살펴보자.
  • 인덱스를 최적화하여 설정했고 페이지 분석을 통해 수정해야 할 특정 쿼리를 찾아냈다면 다음 방법으로 쿼리를 다시 작성 해보자.
  1. 가능한 작은 크기의 쿼리 결과가 반환되도록 로직을 재구성해 보자.
  2. 인덱스가 좀 더 효과적으로 작동할 수 있도록 모델을 재구성해 보자.
  3. SQL이 ORM에 의해 생성된 쿼리보다 더 효과적일 수 있는 부분에 SQL을 직접 이용해 보자.

24.2.4 ATOMIC_REQUESTS 비활성화하기

  • 절대 다수의 장고 프로젝트 사이트에서는 ATOMIC_REQUESTS 설정 값을 True로 설정해도 구동하는데 전혀 문제가 없다. 모든 데이터베이스 쿼리를 트랜젝션으로 처리함에 따라 나타나는 부하 문제는 거의 인지할 수 없는 수준이다.
  • 다만 병목지점을 분석한 결과 트랜젝션에서 너무 많은 지연이 나타나는것을 확인

24.3 데이터베이스의 성능 최대한 이용하기

  • 데이터베이스 접근 최적화에 대해 좀 더 세부적으로 고려해 볼 수 있다.

24.3.1 데이터베이스에서 삼가야 할 것들

  • SQL에 포함되어서는 안되는 두 가지
  1. 로그
  • 데이터베이스에 로그 데이터를 저장하지 말라. 개발 환경에서는 로그 데이터를 DB에 저장해도 큰 문제가 보이지 않는다. 하지만 로그 데이터가 커지면서 많은 양의 데이터가 DB에 추가 됨에 따라 DB 성능이 전체적으로 느려짐을 경험하게 될 것이다.
  1. 일시적 데이터
  • 일시적 데이터를 DB에 저장하지 않기를 바란다. 리라이팅(rewriting)이 이루어지는 데이터의 경우 SQL 이용을 피해야 한다는 의미. 이러한 데이터는 NoSQL을 이용하는것이 좋다.

24.3.2 PostgreSQL 최대한 이용하기

24.3.3 MySQL 최대한 이용하기

  • MySQL은 상대적으로 이용하기 쉬운 서버 중 하나.

24.4 Memcached나 Redis를 이용하여 쿼리 캐시하기

  • 간단한 세팅으로 엄청난 성능 향상을 꾀할 수 있다.
  • 사이트 전반에 적용되는 캐시를 설정할 수도 있고 각 뷰나 템플릿별로 캐시를 할 수도 있다.
  • Django의 저수준 레벨 캐시 API를 이용하여 객체를 캐리할 수도 있다.

24.5 캐시를 이용할 곳 정하기

  • 가장 많은 쿼리를 포함하고 있는 뷰와 템플릿은 어떤 곳인가?
  • 어떤 URL이 가장 많은 요청을 받는가?
  • 캐시를 삭제해야 할 시점은 언제인가?

24.6 서드 파티 캐시 패키지

서드 파티 캐시 패키지는 다음과 같은 기능을 제공한다.

  1. 쿼리세트(QuerySet)캐시
  2. 캐시 삭제 세팅과 메커니즘
  3. 다양한 캐시 백엔드
  4. 기존 캐시 시스템에 대한 대안과 실험적이며 과도기적 방법론
  • 인기 있는 장고 캐시 패키지
    • django-cachepos
    • django-cachalot

24.7 HTML, CSS, JS 압축과 최소화하기

  • 브라우저가 웹 페이지를 렌더링할 때 일반적으로 HTML, CSS, JS, 이미지 파일들이 로드된다. 페이지를 느리게 하는 원인
  • 장고에서 GZipMiddleware, ($spaceless$) 템플릿태그를 지원 BUT 위의 작업을 하느라 시스템 리소스를 너무 많이 사용하여 시스템 병목 지점이 될 수 있다.
  • Nginx, Apache 같은 웹 서버를 이용하여 외부로 나가는 콘텐츠를 압축하는 방법.

24.8 업스트림 캐시나 CDN 이용하기

  • 바니시(Vanish)와 같은 업스트림 캐시(upstream cache)는 매우 유용하다. 웹 서버 앞단에서 구동하며 웹 페이지 또는 콘텐츠 제공 속도를 크게 높인다.

요약

  • 병목 현상에 대해 프로젝트 초기 단계부터 고민할 것인가?
  • 페이지와 쿼리 프로파일링하기
  • 쿼리 최적화
  • 데이터베이스 잘 이용하기
  • 쿼리 캐시하기
  • 어떤 것들을 캐시할 것인가?
  • HTML, CSS, JS 압축하기
  • 기타 다른 참고 자료 살펴보기
반응형
Comments