ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [개념 정리] Django의 ORM과 Lazy-Loading, N+1 Problems, Eager-Loading
    django 2022. 2. 18. 04:50

     

    # ORM이란?

     : ORM은 Object-Relational Mapping의 약자로, 객체(Object)와 관계형 데이터베이스(Relational Database)의 데이터를 매핑(Mapping)해주는 것을 의미한다. 쉽게 말하면 장고의 ORM은 파이썬과 DataBase의 SQL 사이의 통역사 역할을 해준다. 개발자는 장고 ORM을 통해 별도의 SQL 쿼리 없이 파이썬 코드만으로 DB를 조작할 수 있다.

     

    # 예시

    # ORM 문법
    queryset = Book.objects.all()
    
    # <queryset>.query를 문자열로 출력하면 실제 SQL 문을 확인할 수 있다.
    print(str(queryset.query))
    
    # SQL 문법
     >> SELECT "appname_book"."id", "appname_book"."title","appname_book"."author" FROM "appname_book"

    장고 ORM으로는 우리가 흔히 아는 .filter(), .get(), .all(), .exclude(), .annotate(), .aggregate() 등이 있다.


    # Lazy-Loading

     : ORM은 LazyLoading이라는 특징을 갖는데, 여기서 LazyLoading이란 ORM에서 명령을 실행할 때마다 바로 데이터베이스에 접근하여 데이터를 가져오는 것이 아니라, 모든 명령처리가 끝나고 실제 데이터를 불러와야 할 때까지 데이터베이스에 접근하는 것을 최대한 미루는 것을 의미한다. 즉 정말 필요한 시점에 SQL문을 호출하여 데이터베이스에서 데이터를 다룬다.

     

    # 예시

    # Post라는 모델이 있다고 가정
    
    # ORM을 작성했으나 실제로 DB에 접근하지 않음
    post = Post.objects.all()
    
    # 해당 데이터가 필요할 때 DB에 접근함
    print(post[0])

     

    또한 다음 코드를 보면 LazyLoading은 정말 필요할 때 필요한 만큼의 데이터만 가져온다는 것을 알 수 있다.

     아래 사진과 같이 공식 문서를 보면 Slicing, Pickling/Caching, __repr()__, len(), list(), bool()과 같은 상황에서 쿼리셋이 실제 데이터베이스에 접근함을 알 수 있다.


    # N+1 Problems

     : 이러한 LazyLoading은 쿼리를 정말 필요할 때 필요한 만큼만 날려 효율적이지만, N+1 Problems라는 비효율적인 문제를 야기하기도 한다. N+1 Problems는 데이터베이스에 비효율적으로 많이 접근해 응답 속도를 저하시킴을 의미한다.

     

    # 예시

    # Post와 Author가 다대일 관계를 맺고 있다고 가정
    # 모든 post의 author를 출력하는 상황
    # post가 총 n개 있다고 가정
    
    posts = Post.objects.all()
    
    for post in posts:
    	print(post.author)

     위 경우 모든 포스트를 가져오는데 1번, 각 포스트 별로 저자를 불러오는데 N번 DB에 접근해 총 N+1번 쿼리를 날리는 비효율적인 접근 방식을 갖게 된다.


    # Eager-Loading

     : EagerLoading은 즉시 로딩이라는 뜻으로 LazyLoading과 반대되는 개념이다. EagerLoading은 LazyLoading과 달리 데이터가 실제로 쓰일 때 쿼리를 날리는 게 아니라 ORM이 작성되는 그 순간 바로 쿼리를 날린다. 대표적인 ORM으로 select_related(), prefetch_related() 등이 있다. 이 EagerLoading은 LazyLoading의 장점을 갖진 못하지만 N+1 Problems를 해결해준다.

     

    # 예시

    # 위에서 N+1번 DB에 접근하는 것을 아래 코드는 1번만 DB에 접근해서 해결할 수 있다.
    
    posts = Post.objects.select_related("author").all()
    
    for post in posts:
    	print(post.author)

    ORM은 기본적으로 한번 DB에 쿼리를 날리면 그 결과물을 (쿼리셋) 캐시에 저장한다.

    위 N+1 Problems 예시와 달리, 이 예시 코드는 select_related()를 활용하여 모든 포스트들을 가져올 때 author도 같이 불러와 캐시에 저장한다. 이후 post.author로 Author 모델을 호출해도 이미 캐시에 해당 데이터들이 저장되어 있기 때문에 DB에 접근하지 않는다. 따라서 위 EagerLoading은 DB에 1번만 접근한다.

     

    이처럼 ORM의 LazyLoading 특성을 바탕으로, 상황에 맞게 EagerLoading을 활용해주면 DB에 접근하는 횟수를 최적화하고 서버의 속도를 높일 수 있다.


    참고

    https://htkim298.medium.com/django-%EC%9E%A5%EA%B3%A0-orm-%EC%A0%95%EB%A6%AC-cc7c6390cf1

    http://www.incodom.kr/Django_ORM

    https://velog.io/@anjaekk/Django-Django-ORM%EC%9D%98-Lazy-loading%EA%B3%BC-N1-Query-%EB%AC%B8%EC%A0%9C

    https://leffept.tistory.com/311

     

Designed by Tistory.