-
[CS study] 2022.06.08CS 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