-
엔티티의 1:N 관계에서의 QueryDsl fetch Join - (2) 페이지네이션 해결 방법Back-End/Querydsl 2022. 12. 27. 16:00728x90
사실, 이 문제에 대한 해결법에 대해서 많이 구글링을 해 보았지만, 상황에 따라 굉장히 해결법이 다양했다.
그 중에서도 가장 확실하고, 보편적으로 쓰일 만 한 방법을 소개한다.
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
'Back-End > Querydsl' 카테고리의 다른 글
엔티티의 1:N 관계에서의 QueryDsl fetch Join - (1) 페이지네이션 (0) 2022.12.21 Querydsl 및 간단한 작성방법 (0) 2022.04.21