Spring
SpringData JPA, N+1 문제를 해결
sehunbang
2024. 3. 6. 20:28
N+1 문제란?
엔티티 하나을 조회하기 위해서 1:N 로 연관된 엔티티까지 조회 쿼리문이 N+1번 날라가는 이슈
이로 인해 아래와 같은 시스템에 심각한 성능 저하가 일어날 수 있다
- Comment 조회 - 1번
- Comment의 갯수(각 Comment가 가지고 있는 Board 조회) - N번
이렇게 하면 N+1번의 쿼리가 발생하는 것이다
해결방법 3가지:
- GlobalFetch
- Fetch Join
- EntityGraph
Global Fetch Strategy
글로벌 패치 전략이란, 엔티티를 생성할 때(컴파일 시점) 결정 되는 연관관계 전략이
해결방법은 @ManyToOne 속성에 fetch 속성으로 LAZY를 주면 된다
public class Comment {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "thread_id")
Board thread;
}
Fetch Join
조인할 때 연관된 엔티티나 컬렉션를 함께 조회하려고 할 때 사용한다 결과는 `EAGER`와 똑같지만 과정은
다르다 `EAGER`의 경우에는 N+1 쿼리가 발생하지만
Fetch Join 의 경우에는 한번이 쿼리문으로 해결이 가능하다.
Spring Data JPA 에서는 `@Query` 어노테이션을 이용하여 JPQL를 생성할 수 있다 사용하는 방법은 위와 동일하게 `join fetch` 뒤에 연관된 엔티티나 컬렉션을 적어주면 된다
public interface CommentRepository extends JpaRepository<Comment, Long> {
@Query("select c from Comment c join fetch c.board")
List<Comment> findAll();
}
EntityGraph - 현업 사용 (+ QueryDSL도 현업 사용)
@EntityGraph도 마찬가지로 EntityGraph 상에 있는 Entity들의 연관관계 속에서 필요한 엔티티와 컬렉션을 함께 조회하려고 할때 사용한다
public interface CommentRepository extends JpaRepository<Comment, Long> {
@EntityGraph(attributePaths = {"thread"}, type = EntityGraph.EntityGraphType.LOAD)
List<Comment> findAll();
}
- Spring Data JPA에서 적용하려는 메소드 위에 `@EntityGraph` 어노테이션을 달고 옵션을 준다
- attributePaths는 같이 조회할 연관 엔티티명을 적으면 된다.
- (콤마)를 통하여 여러개를 줄 수도 있다
type은 EntityGraphType.LOAD, EntityGraphType.FETCH 2가지가 있다
- LOAD : attributePaths에 정의한 엔티티들은 EAGER, 나머지는 글로벌 패치 전략에 따라 패치한다
- 일단 attributePaths 는 EAGER, 나머지는 매핑 설정 따라서
- FETCH : attributePaths에 정의한 엔티티들은 EAGER, 나머지는 LAZY로 패치한다
- 나머지 는 FETCH 다 LAZY!!