https://jojoldu.tistory.com/165
JPA N+1 문제 및 해결방안
안녕하세요? 이번 시간엔 JPA의 N+1 문제에 대해 이야기 해보려고 합니다. 모든 코드는 Github에 있기 때문에 함께 보시면 더 이해하기 쉬우실 것 같습니다. (공부한 내용을 정리하는 Github와 세미나+
jojoldu.tistory.com
하위 엔티티들을 첫 쿼리 실행시 한번에 가져오지 않고, Lazy Loading으로 필요한 곳에서 사용되어 쿼리가 실행될때 발생하는 문제가 N+1 쿼리 문제
- join fetch를 사용하는 것
@Query("select a from Academy a join fetch a.subjects")
이 방법은 불필요한 쿼리문이 추가되는 단점이 있습니다.
- 2번째 방법은 @EntityGraph을 사용하는 것
@EntityGraph(attributePaths = "subjects")
JoinFetch는 Inner Join, Entity Graph는 Outer Join이라는 차이점이 있음
공통적으로 카테시안 곱(Cartesian Product)이 발생하여 Subject의 수만큼 Academy가 중복 발생
- 필드의 타입을 Set으로 선언하는 것(LinkedHashSet을 사용하여 순서를 보장)
- 2번째 방법은 distinct를 사용하여 중복을 제거하는 것
https://jojoldu.tistory.com/457
MultipleBagFetchException 발생시 해결 방법
JPA의 N+1 문제에 대한 해결책으로 Fetch Join을 사용하다보면 자주 만나는 문제가 있습니다. 바로 MultipleBagFetchException 입니다. 이 문제는 2개 이상의 OneToMany 자식 테이블에 Fetch Join을 선언했을때 발..
jojoldu.tistory.com
MultipleBagFetchException
이 문제는 2개 이상의 OneToMany 자식 테이블에 Fetch Join을 선언했을때 발생합니다.
@Query("SELECT s " +
"FROM Store s " +
"JOIN FETCH s.products " +
"JOIN FETCH s.employees")
List<Store> findAllByFetchJoin ();
해결책은 하이버네이트의 default_batch_fetch_size 옵션
(1개씩 사용되는 조건문을 in절로 묶어서 조회)
테스트 해보자
@TestPropertySource(properties = "spring.jpa.properties.hibernate.default_batch_fetch_size=1000") // 옵션 적용
만약 1만개의 Store (부모 엔티티)가 조회된다면
옵션 적용시 (1000개)
Store 조회 쿼리 1번
각 Store의 Product 조회 쿼리가 10번 (10,000 / 1,000)
각 Store의 Employee 조회 쿼리가 10번 (10,000 / 1,000)
= 21번
보통 옵션값을 1,000 이상 주지는 않습니다.
in절 파라미터로 1,000 개 이상을 주었을때 너무 많은 in절 파라미터로 인해 문제가 발생할수도 있기 때문
Querydsl이라고해서 용빼는 재주가 있는게 아닙니다.
결국 Querydsl 역시 JPQL 로 변환 작업에서 동일하게 문제가 발생합니다.
Querydsl 역시 N 관계의 자식들에 대해서는 멀티 페치 조인이 안됩니다.
hibernate.default_batch_fetch_size를 글로벌 설정으로 사용해 N+1 문제를 최대한 in 쿼리로 기본적인 성능을 보장하게 한다.
@OneToOne, @ManyToOne과 같이 1 관계의 자식 엔티티에 대해서는 모두 Fetch Join을 적용하여 한방 쿼리를 수행한다.
@OneToMany, @ManyToMany와 같이 N 관계의 자식 엔티티에 관해서는 가장 데이터가 많은 자식쪽에 Fetch Join을 사용한다.
Fetch Join이 없는 자식 엔티티에 관해서는 위에서 선언한 hibernate.default_batch_fetch_size 적용으로 100~1000개의 in 쿼리로 성능을 보장한다.
'프로그래밍 > JPA' 카테고리의 다른 글
[Tutorial] JPA With Postgres and SpringBoot (0) | 2019.08.08 |
---|---|
SpringBoot JPA (feat. h2 db) (0) | 2019.07.03 |