JPA :: fetch join을 통한 컬렉션 N+1 문제 해결법

숑숑·2022년 2월 17일
0

JPA

목록 보기
3/4
post-thumbnail

📌 컬렉션 조회에서의 N+1?

  • 지연로딩 적용 시, 컬렉션 또한 따로 조회해줘야 한다.
  • N+1 문제가 발생하기 더 쉽고, 일대일/다대일보다 훨씬 대응이 까다롭다.

Order 엔티티가 OrderItem 엔티티의 컬렉션을 필드로 가지고 있다고 가정한다.

📌 해결 코드

public List<Order> findAllWithItem() {
 return em.createQuery(
 "select distinct o from Order o" +
 " join fetch o.member m" +
 " join fetch o.delivery d" +
 " join fetch o.orderItems oi" +
 " join fetch oi.item i", Order.class)
 .getResultList();
}
  • 무려 단일 쿼리로 해결하는 방법이다.
  • 단순하게, 컬렉션 또한 fetch join 하여 가져온다.
  • distinct 키워드를 통해, Order 엔티티를 기준으로 중복 제거 후 반환도 가능하다.

그러나, 치명적인 단점이 있다.

fetch join을 통한 컬렉션 조회는, 페이징 처리가 큰 리스크가 된다.

  • 이 경우, 모든 페이징 처리를 메모리에서 하기 때문이다!
  • row 수가 많을 수록 메모리를 고갈시키므로, 큰 장애로 이어질 수 있다.

📌 페이징 한계 극복법

  1. 먼저 N:1 관계는 모두 fetch join 한다. row 수에 영향을 주지 않으므로 페이징 쿼리에 아무 영향이 없다.
  2. 컬렉션은 어쩔 수 없이 따로 지연로딩 처리 해준다.

단, batch를 이용해 지연로딩한다.

내부적으로 IN 쿼리를 이용하므로 성능 최적화에 좋다.

예를 들어, 만개 row의 내부 컬렉션을 모두 조회한다고 가정하면,
batch_size가 1000이라면 단 10번만 조회해도 된다.
반대로, batch_size가 1이라면 만번의 쿼리 요청이 일어나게 된다.

profile
툴 만들기 좋아하는 삽질 전문(...) 주니어 백엔드 개발자입니다.

0개의 댓글