Django와 같은 ORM을 사용하여 DB에 접근하다 보면 불필요한 DB의 접근이 많이 발생할 수 있다.
QuerySet의 특징으로 인해서 성능 문제가 발생할 수 있는데, 이를 이해하고 효율적인 처리를 하도록 최적화하는 과정은 필수적이다.
를 하기 위해서는 실제로 어떻게 실행되고 있는지를 확인하는 것이 필요하다.
다양한 툴이 있지만, QuerySet.explain()을 사용할 수도 있고, django-debug-toolbar와 같은 라이브러리를 활용하는 방법도 있겠다.
django의 성능 이슈와 관련해서 다음을 이해하는 것이 중요하다.
Lazy Query
QuerySet의 계산 시점
데이터 캐싱
QuerySet 캐싱 뿐만 아니라 ORM 객체에 대한 캐싱도 있다.
일반적으로 호출할 수 없는 attribute라면 캐싱된다.
예를들어 모델 객체의 인스턴스에서 필드값을 attribute로 호출하면 이것은 캐싱된다.
하지만 일반적으로 호출하는 all()과 같은 속성은 매번 DB를 히트한다.
과 같은 경우에 SQL의 생성이 적절하지 않다면 RawSQL class로 일부 SQL을 명시적으로 넣거나, raw SQL로 완전히 SQL을 사용할 수도 있다.
get()을 사용하여 개별 객체를 검색할 때 unique 또는 db_index열을 사용하는데 두가지 이유가 있다.
DB 인덱스로 쿼리 속도가 더 빠르다.
또한 여러 객체가 조건과 일치하면 쿼리가 훨씬 느릴 수 있다.
때문에 열에 고유한 조건이 있다면 이러한 현상이 일어나지 않을 것이고, get()을 사용할 때는 unique, db_index 된 열을 사용한다.
조회는 단 하나의 객체가 반환될 것이라고 보장하지 않는다.
쿼리가 두개 이상의 객체와 일치하면 DB에서 모든 객체를 검색하여 전송한다.
데이터가 많다면 이로 인한 성능저하는 더욱 심각해 질 것이다.
>> 다시말해 name = "str"의 형태 보다는 id = 000 의 형태가 속도가 빠르다는 것!
>> 인덱싱도 되어있고, 고유한 값임을 알고 있기 때문.
일반적으로 데이터 집합의 여러 부분에 대해 DB를 여러번 히트하는 것은 하나의 쿼리에서 모두 검색하는 것 보다 효율적이지 않다. 특히나 이런 상황은 루프에서 실행되는 쿼리의 경우에 특히 중요하다(N+1)
select_related() 또는 prefetch_related()를 이해하고 사용하자.
manager의 사용여부를 숙지해야한다.
dict 혹은 list의 값을 원할 때, ORM 모델 객체가 필요하지 않은 경우에는 values()를 적절히 사용한다.
DB의 열이 필요하지 않다는걸 알고 있거나, 로드되지 않도록할 때 사용한다. 하지만 부적절하게 사용시 ORM이 별도로 쿼리를 날리게될 수 있다.
개수만 원할 경우 len(queryset)보다 빠르다.
적어도 하나의 결과가 존재하는지에 대한 확인, if 문보다 적절하다.
개별적으로 저장하는 대신 대량 SQL UPDATE문을 사용한다. 삭제일 경우에도 동일.
그러나 일괄 업데이트 메서드는 개별 인스턴스의 save(), delete()를 호출할 수 없다.
entry.person.id (x)
entry.person_id (o)
가능한 경우 bulk_create()를 사용하여 쿼리 수를 줄인다.
출처 : https://blog.myungseokang.dev/posts/database-access-optimization/