ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 엔티티의 1:N 관계에서의 QueryDsl fetch Join - (2) 페이지네이션 해결 방법
    Back-End/Querydsl 2022. 12. 27. 16:00
    728x90

     

     

     

     

    사실, 이 문제에 대한 해결법에 대해서 많이 구글링을 해 보았지만, 상황에 따라 굉장히 해결법이 다양했다.

     

    그 중에서도 가장 확실하고, 보편적으로 쓰일 만 한 방법을 소개한다.

     

     


     

    fetch Join + @BatchSize 활용하기

     

     

     

    먼저, fetch Join을 통해 ToOne 관계에 있는 모든 entity를 fetch join 한다.

     

    ToOne관계의 경우, row수를 증가시키지 않기 때문에 페이징 쿼리에 영향을 주지 않기 때문이다.

     

    이 후, 컬렉션은 지연 로딩으로 조회를 한다.

     

    여기서, 지연 로딩 성능을 최적화하기 위해서 @BatchSize를 사용하게 된다.

     

     

    BatchSize의 경우, 어노테이션을 사용하여 개별로 적용할 수도 있고  application.yml 파일 내에 설정을 통해서 글로벌 적용도 가능하다.

     

     

     

    글로벌하게 적용하는 경우

     

    spring:
      jpa:
        properties:
          hibernate:
            default_batch_fetch_size: 100 // 지연 로딩된 컬렉션을 실제 조회 할때, 쿼리의 in 절에 들어가는 외래키의 수

     

    일반적으로 권유가 되는 숫자는 100~500이며, 최대 1000까지 사용하는 것이 좋다고 한다.

     

    쿼리를 통해 가져오는 row와 쿼리의 갯수 사이에는 Trade-off가 있기 때문에, 성능을 생각해서 잘 분배를 해야 한다.

     

    사실 페이징이라는 개념이 들어간 이상, 100개가 넘는 아이템을 한 페이지에 조회하는 경우는 쉽게 오지 않을 것이라고 생각하기 때문에

     

    100개가 적당하다고 생각이 된다.

     

     

    개별로 적용하는 경우

     

     

     

    Item Entity와 Option Entity가 1:N의 관계를 가지고 있다고 가정하자.

     

     

    그리고 서비스 로직에서, 특정 카테고리 id를 가진 아이템을 가져올 것이다.

     

    그리고 그에 상응하는 item은 총 3개가 있다고 가정한다.

     

    {
        ...
        List<Item> items = itemRepository.findByCategoryId(categoryId);
            for (Item item1 : items) {
                System.out.println(item1.getOptions().size());
            }
        ...
    }

     

    다음과 같이 3개의 item 객체를 불러온 후,

     

    각 item에 해당하는 option들을 불러온다.

     

    여기서 options에 지연 로딩이 설정되어 있으므로 각 item마다 쿼리가 한 방씩 총 3방이 나가게 된다.

     

    만약 item이 100개 정도가 된다면, 1+100의 쿼리가 나가게 된다.

     

    여기서 @BatchSize를 적용 해 보면

     

     

     

    다음과 같이 기존처럼 3개의 쿼리가 나가지 않고, in절을 활용하여 1개의 쿼리만 나가게 된다.

     

     

    (is_deleted는 soft delete를 적용하는 과정에서 사용되는 필드이므로 무시하면 된다.)

     

    다음과 같이 in을 통해 3개의 쿼리가 나갈 것을 1번의 쿼리로 최적화를 시킬 수 있다.

     

     

     


     

    물론 이 외에도 상황에 따라 다르겠지만,

     

     

    Many쪽에서 fetch를 하는 방법도 있다고 한다.

     

     

     

     

    이전 포스팅 : 엔티티의 1:N 관계에서의 QueryDsl fetch Join - (1) 페이지네이션

     

     

     

     

     

    Ref.

     

     

    https://bcp0109.tistory.com/304

     

     

    댓글

Designed by Tistory.