ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [CS study] 2022.06.08
    CS 2022. 6. 8. 10:34

    Django orm Lazy-loading,  N+1 문제

     

    Lazy-loading

    • django의 orm은 Lazy-loading 방식을 사용한다.
    • Lazy-loading이란 orm에서 명령을 실행할 때마다 데이터베이스에 접근하여 데이터를 가져오는 것이 아닌 모든 명령 처리가 끝나고 실제 데이터를 불러와야 할 때 데이터베이스 쿼리문을 실행하는 방식
    • django에서 쿼리문을 실행하는 시점
      • 슬라이싱
      • Picking/ Cashing
      • __repr()___
      • len()
      • list()
      • bool()
      • if문을 사용해 boolean값을 확인할 때 그러므로 찾는 값의 존재 여부만 파악할 때는. exist()를 이용하여 확인하는 게 효율적

     

    Eager-loading

    • 즉시 로딩 Lazy-loading의 반대 개념
    • 지금 당장 사용하지 않을 데이터도 포함하여 쿼리문을 실행한다
    • Lazy-loading의 N+1 문제 해결책으로 많이 사용하게 됨
    • djagno에서 사용하려면 select_related와 prefetch_related메서드를 사용한다

     

    N+1 쿼리 문제

    • 외래 키를 참조해서 데이터를 가져올 때 문제가 발생한다.

     

    prefetch_related

    • many 관계에서 사용하는 걸 권장 ( 그러나 many관계, OneToOne관계 모두 사용 가능)
    • WHERE IN () 절을 사용해서 쿼리를 한번 더 한다.
    • assertNumQueries()로 쿼리 횟수 검증하기
    def get_article_list(offset: int, limit: int) -> QuerySet[Article]:
        return Article.objects.order_by("-id").prefetch_related("like_set")[offset : offset + limit]
    def test_get_article_list_should_prefetch_like(self) -> None:
            # Given
            user = User.objects.create(name="test_user")
            articles = [Article.objects.create(title=f"{i}") for i in range(1, 21)]
            do_like(user.id, articles[-1].id)
    
            # When
            with self.assertNumQueries(2):
                result_articles = get_article_list(0, 10)
                result_counts = [a.like_set.count() for a in result_articles]
    
                # Then
                self.assertEqual(len(result_articles), 10)
                self.assertEqual(1, result_counts[0])
                self.assertEqual(
                    [a.id for a in reversed(articles[10:21])],
                    [a.id for a in result_articles],
                )

     

    select_related

    • OneToOne 혹은 foreign key 관계에서 사용하자 (many에서 쓰면 에러)
    • join을 사용하기 때문에 추가 쿼리가 일어나지 않는다.
    Article.objects.prefetch_related("like_set").all()
    
    Article.objects.select_related("like_set").all() # Error!
    Like.objects.select_related("user").all()  # 쿼리가 1번 일어남
    
    Like.objects.prefetch_related("user").all(). # 에러는 안 나지만 쿼리가 2번 일어남
    
    def test_temp(self) -> None:
          # Given
          user = User.objects.create(name="user1")
          article1 = Article.objects.create(title="artice1")
          like = Like.objects.create(user_id=user.id, article_id=article1.id)
          Article.objects.create(title="article2")
    
          # Expect
          with self.assertNumQueries(1):
              result_like = Like.objects.select_related("user").get(id=like.id)
              self.assertEqual(like.id, result_like.user.id)
    
          # Expect
          with self.assertNumQueries(2):
              result_like = Like.objects.prefetch_related("user").get(id=like.id)
              self.assertEqual(like.id, result_like.user.id)

    'CS' 카테고리의 다른 글

    [CS study] 2022.06.17  (0) 2022.06.17
    [CS study] 2022.06.13  (0) 2022.06.13
    [CS study] 2022.06.01  (0) 2022.06.01
    [CS study] 2022.05.30  (0) 2022.05.31
    [CS study] 2022.05.22  (0) 2022.05.22

    댓글

Designed by Tistory.