컬렉션 조회 최적화

2023. 9. 29. 00:08LECTURES/실전! 스프링 부트와 JPA 활용

주문 조회 V1 : 엔티티 직접 노출

https://github.com/dpdms529/JpaShop/commit/3971dc6b9dc468984b367f0e171eba544a1f778f

주문 조회 V2 : 엔티티를 DTO로 변환

https://github.com/dpdms529/JpaShop/commit/105d4ee3a72114529e7eb4e048f9e746e5d335c8

  • 지연 로딩으로 너무 많은 SQL 실행

주문 조회 V3 : 엔티티를 DTO로 변환 - 페치 조인 최적화

https://github.com/dpdms529/JpaShop/commit/203db80c6a987b83bcfc739a19dc0a0dddda80b1

  • fetch join으로 SQL이 1번만 실행됨
  • 일대다 조인으로 다를 기준으로 row가 증가하므로 distinct를 사용해 중복을 제거해주어야 함
    • 스프링부트 3버전부터는 Hibernate 6버전을 사용하면서 페치 조인 사용시 자동으로 중복을 제거하도록 변경됨
  • 단점 : 페이징 불가능
    • 모든 데이터를 DB에서 읽어온 후 메모리에서 페이징해버림
    • 데이터가 많으면 오버플로우

주문 조회 V3.1 : 엔티티를 DTO로 변환 - 페이징과 한계 돌파

https://github.com/dpdms529/JpaShop/commit/ebaa739fe67f2fb0994664257cbb33041ce575c2

  • ToOne 관계를 모두 페치 조인 -> row 수를 증가시키지 않음
  • 컬렉션은 지연 로딩으로 조회
  • 지연 로딩 성능 최적화를 위해 hibernate.default_batch_fetch_size, @BatchSize 적용
    • 컬렉션이나, 프록시 객체를 한꺼번에 설정한 size만큼 IN 쿼리로 조회
    • hibernate.default_batch_fetch_size : 글로벌 설정
    • @BatchSize : 개별 최적화, 컬렉션은 컬렉션 필드에, 엔티티는 엔티티 클래스에 적용
    • hibernate 6.2부터는 in 대신 array_contains를 사용해서 성능을 최적화함
      • in을 사용하면 바인딩 변수 개수에 따라 쿼리가 달라짐 -> 매번 하드 파싱해야 함
      • array_contain는 바인딩 변수가 1개이기 때문에 쿼리가 변하지 않음 -> 소프트 파싱 가능
  • 장점
    • 쿼리 호출 수가 1 + N에서 1 + 1로 최적화됨
    • 조인보다 DB 데이터 전송량이 최적화됨
    • 페치 조인 방식과 비교해서 쿼리 호출 수가 약간 증가하지만, DB 데이터 전송량이 감소
    • 컬렉션 페치 조인은 페이징이 불가능하지만 이 방법은 페이징 가능
  • default_batch_fetch_size 크기 설정
    • 100~1000 사이를 선택하는 것을 권장
    • 데이터베이스에 따라 IN 절 파라미터를 1000으로 제한하기도함
    • 순간 부하를 견딜 수 있는만큼 설정

주문 조회 V4 : JPA에서 DTO 직접 조회

https://github.com/dpdms529/JpaShop/commit/ca9dda7a4b2fb2fdf5643c7d784517e6ad2f4477

  • ToOne 관계들을 먼저 조회하고, ToMany 관계는 별도로 처리
  • row 수가 증가하지 않는 ToOne 관계는 조인으로 최적화 하기 쉬우므로 한번에 조회하고, ToMany 관계는 최적화하기 어려우므로 별도의 메서드로 조회
  • 루트 1번, 컬렉션 N번 쿼리 실행

주문 조회 V5 : JPA에서 DTO 직접 조회 - 컬렉션 조회 최적화

https://github.com/dpdms529/JpaShop/commit/2586173a8ba6857879aa372eb35385e7b8427b06

  • ToOne 관계들을 먼저 조회하고, 여기서 얻은 식별자로 ToMany 관계를 한꺼번에 조회
  • Map을 사용해서 매칭 성능 향상(O(1))
  • 루트 1번, 컬렉션 1번 쿼리 실행

주문 조회 V6 : JPA에서 DTO로 직접 조회 , 플랫 데이터 최적화

https://github.com/dpdms529/JpaShop/commit/3f23e2be7cefd462013bea3c8356b0b1fcf3430a

  • 쿼리 1번 실행
  • 단점
    • 조인으로 인해 DB에서 애플리케이션으로 전달하는 데이터에 중복 데이터가 추가되므로 상황에 따라 V5보다 더 느릴 수 있음
    • 애플리케이션에서 추가 작업이 큼
    • 페이징 불가능

API 개발 고급 정리

권장 순서

  1. 엔티티 조회 방식으로 우선 접근
    1. 페치 조인으로 쿼리 수 최적화
    2. 컬렉션 최적화
      • 페이징 필요 -> hibernate.default_batch_fetch_size, @BatchSize 로 최적화
      • 페이지 필요 X -> 페치 조인 사용
  2. 엔티티 조회 방식으로 해결이 안되면 DTO 조회 방식 사용
  3. DTO 조회 방식으로 해결이 안되면 NativeSQL or 스프링 JDBC Template

DTO 조회 방식의 선택지

  • V4
    • 코드가 단순
    • 특정 주문 한건만 조회하면 이 방식을 사용해도 성능이 잘 나옴
  • V5
    • 코드가 복잡
    • 여러 주문을 한꺼번에 조회하는 경우에 사용
  • V6
    • 쿼리 한번으로 최적화됨
    • 일대다 관계에서 일을 기준으로 페이징 불가능 -> 실무에서 선택하기 어려움
    • 데이터가 많으면 중복 전송이 증가해서 V5와 비교해서 성능 차이도 미비

 

 

실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화 - 인프런 | 강의

스프링 부트와 JPA를 활용해서 API를 개발합니다. 그리고 JPA 극한의 성능 최적화 방법을 학습할 수 있습니다., 스프링 부트, 실무에서 잘 쓰고 싶다면? 복잡한 문제까지 해결하는 힘을 길러보세요

www.inflearn.com

'LECTURES > 실전! 스프링 부트와 JPA 활용' 카테고리의 다른 글

실무 필수 최적화  (0) 2023.09.29
지연 로딩과 조회 성능 최적화  (0) 2023.09.29
API 개발 기본 / 고급 준비  (0) 2023.09.29
웹 계층 개발  (0) 2023.09.29
주문 도메인 개발  (0) 2023.09.29