Python/Django

[Django] Queryset 최적화 기법 (select, prefetch, F, Indexing 등)

yubi5050 2022. 8. 11. 04:05

사전 학습 내용

https://yubi5050.tistory.com/60 // ORM 이란?

https://yubi5050.tistory.com/83 // ORM Queryset API 

https://yubi5050.tistory.com/130 // ORM Queryset 특징

 

QuerySet 최적화 기법 (1) select_related & prefetch_related

일반적으로 조회(GET)시 or Serailization 과정에서 많이 발생

ORM의 Lazy Loading 특성에서 발생하는 N+1 Problem 등 의 문제를 극복하기 위해 Eager Loading (즉시 로딩) 진행

 

📁 select_related

  • ForeignKey(1:M)의 1입장에서, OneToOne 관계 등에서 사용 가능
  • SQL의 JOIN절을 사용하는 방법

 

📁 prefetch_related

  • ForeignKey(1:M)의 M입장에서, ManyToMany 관계 등에서 사용 가능
  • SQL의 WHERE … IN 구문을 사용하는 방법
  • 만일 Table에서 역참조의 관계를 가질 경우 '필드명_set' 으로 참조

 

사용 예시

 

QuerySet 최적화 기법 (2) F() Expression

📁 F() expression 

  • 목적 : race conditon (경쟁 상황)을 피하기 위해 사용 & 동시성 이슈
  • 상황 : 다중의 프로세스에서 같은 영역에 엑세스 하여 처리(조회, 변경 등)를 시도할 때
    ex) 동시에 여러명의 유저가 특정 글에 접근하여 조회수가 업데이트 되야 하는 상황
  • F expression은 파이썬 메모리 로드없이, DB 단에서 작업을 처리 → 경재 상황 해결 및 퍼포먼스 향상

 

QuerySet 최적화 기법 (3) Indexing 방법

  • 추후 Update

 

QuerySet 최적화 기법 (4) 기타

📁 QuerySet.count 활용

  • queryset의 길이를 호출 시 => len(queryset) 보다 count() 활용하기
  • count() : DB에서 값을 계산해서 반환
  • len() : lazy 메모리 로딩 -> 연산 

 

📁 QuerySet.exists 사용

  • Queryset이 1개라도 존재하는지 체크할 경우
  • if Queryset 보다 exists() 활용하기
  • 위 count()와 동일 이유

 

📁 외래키 값 직접 사용

  • 외래 키 id 값만 필요한 경우
  • Proejct.comment.id 말고 Project.comment_id 사용

차이점

  • Project.comment.id : ForeignKey를 참조한 테이블에서 Object의 id를 가져옴)
  • Project.comment_id : ForeignKey 자체에서 가져옴)

 

📁 values & values_list

  • dict 또는 list 값을 원할 때, ORM 모델 객체가 필요하지 않은 경우 values() 를 적절하게 사용 가능

 

📁 QuerySet.defer & only

  • 데이터베이스 불필요 열(column)이 로드되지 않도록 defer()와 only() 사용

 

📁 all() => filter() 로직 지양

  • db 전체 호출 all() 다음 부분 호출 filter() 사용시 caching 안됨.

 

📁 개별 개체 정보 조회시 unique 필드나 index 열을 사용 

  • get()을 사용한 개별 객체를 검색시 unique 또는 db_index 열을 사용
  • unique 필드나 Index로 검색시 여러 조건들에 해당되는 객체가 필터링 되지 않기 때문에 Query 속도 최적

 

📁 일괄 삽입, 삭제 메소드 : bulk_xxx() 사용

  • 대량 삽입 (bulk_create), 대량 삭제(bulk_delete) 사용시 Query 문 갯수가 감소

 

 

📁 bulk_update() 사용시 주의점

  • bulk_create는 id 값을 반환하지 않음, 
  • bulk로 생성된 id만 필요한 로직 구현시, 객체 목록 조회 -> bulk 생성 -> 앞에서의 객체 목록 제외한 객체 목록 조회
  • 다소 비효율적인 조회가 호출됨 주의

 

📁 ManytoMany Field 일괄 add()

  • ManytoMany Field인 Bookmark의 add 함수 이용 일괄 추가 Query 문 갯수 감소

 

 

 

📁 메모리 이슈 발생시 iterator() 사용

  • 엑세스 해야 하는 객체가 많은 경우 QuerySet의 캐싱 동작으로 인해 많은 양의 메모리가 사용될 수 있음
  • Iterator() 를 활용하여 필요한 양 로드시 성능 향상 및 메모리 감소 효과

 

QuerySet 최적화 기법 (6) 추가 고려 사항

📁 기타 Python이 아닌 DB에서 실행되는 로직 살펴보기

  • filter()  exclude()를 사용하여 DB에서 필터링하는 경우
  • DB에서 aggregation을 하기 위해 어노테이션하는 경우

 

📁 Query Debugger 기능 OFF

 

참고 문헌