본문 바로가기
Django

[Django] Django ORM - N+1 problem

by 혀넝 2022. 4. 9.
  • django orm은 lazy_loading 방식으로, orm에서 명령을 실행할 때마다 데이터베이스에서 데이터를 가져오는 것이 아니라 데이터를 불러와야 할 시점에 데이터베이스에 접근해서 쿼리를 실행한다.
  • 따라서 n개의 모델을 가져오고 n개의 모델을 순회하면서 관련된 모델에 접근할 때, 데이터베이스에 다시 호출을 하게 되어 다시 쿼리를 n번 추가 수행하게 되어 문제가 발생하게 된다.
  • 해당 문제의 해결 방안으로 Eager-Loading이 있다. Eager_Loading을 설정하면 즉각 로딩으로 설정할 수 있다. 그렇게 되면 지금 당장 사용하지 않는 데이터도 포함해 query문을 실행하기 때문에 n+1 문제를 해결하는 해결책으로 사용된다.
  • Eager-Loading 방식으로 데이터를 불러오는 방법으로는 select_related와 prefetch_related의 방법 두 가지가 있다.
  • 두 방법은 sql 문을 복잡하게 만들지만 데이터베이스에 다시 접근하지 않아서 query의 개수를 줄여 성능 향상으로 이어질 수 있다.
  • 두 방법의 공통점: 하나의 쿼리셋을 가져올 때 연관된 오브젝트들을 미리 불러와 데이터베이스에 접근하는 횟수를 줄여 성능을 향상해준다. 또한 result_cache라고 하는 sql의 수행 결과를 저장하는 곳에 cache가 저장되어 필요할 때 사용할 수 있게 해 주고 결국 중복 호출을 방지할 수 있게 해 준다.
  • select_related: one-to-many 관계에서 foreign key를 물고 있는 N, one-to-one 관계와 같이 single_valued relationships, 즉 정참조에서 사용 가능하다. sql에서 join을 사용하는 방법과 같다.
  • prefetch_related: foreign key, one-to-one 뿐만 아니라 many-to-many, many-to-one 등의 모든 관계에서 사용 가능하다. 때문에 주로 select_related가 사용할 수 있는 many-to-many 관계에서 주로 사용한다. sql의 where in을 사용하는 방법과 같다.
.select_related("category") 
.prefetch_related("productingredient_set__ingredient","productfeelings_set__feeling", "productskintype_set__skin_type", "productimage_set")