N+1 문제는 ORM을 사용할 때 흔히 발생하는 성능 문제다.
데이터베이스에서 관련된 엔티티를 조회할 때 발생한다. 기본적으로 한 번의 쿼리로 조회해야 할 데이터를 여러 번 나눠서 쿼리함으로써 쿼리 성능이 저하된다.
N+1 문제의 원리
- 주 쿼리
- 특정 엔티티를 조회하는 쿼리를 한 번 수행한다.
- 하위 엔티티의 반복 조회
- 조회된 각 부모 엔티티에 연관된 하위 엔티티를 가져오려 할 때 (예를 들어 OneToMany로 연결된 User : Post, User 안에 List<Post> 형태로 선언되어있다.) User 한 건당 추가 쿼리(N번)이 발생한다.
N+1 문제의 해결 방법
- Fetch Join 사용
- @Query 또는 Fetch Join을 사용해 한 번의 쿼리로 모든 데이터를 가져오도록 설정한다.
- 예를 들어
SELECT u FROM User u JOIN FETCH u.posts
와 같이 작성하여 User와 관련된 Post를 한 번에 가져올 수 있다. - 이 방법은 단순 조회에는 적합하지만, 너무 많은 데이터를 한 번에 가져오는 경우 성능에 부담이 될 수 있다.
- Entity Graph 사용
- @NamedEntityGraph 또는 @EntityGraph를 통해 엔티티를 설정하여 필요한 연관 엔티티를 미리 지정하여 조회할 수 있다.
- 특정 상황에서 필요한 필드만 선택적으로 로딩하므로 효율적이다.
- Batch Fetching
- Hibernate에서 제공하는 설정이다.
- 한 번에 가져올 엔티티의 수를 배치로 설정할 수 있다.
@BatchSize 어노테이션을 사용하거나 Hibernate 설정 파일에
hibernate.default_batch_fetch_size
를 설정해 사용할 수 있다. - 관련된 엔티티를 한 번에 일정 수량만큼 로드하여 쿼리 횟수를 줄일 수 있다.
- Lazy Loading과 Eager Loading 전략 사용
- 각 연관 관계에 대해
@OneToMany(fetch = FetchType.LAZY)
처럼 필요 시점에 데이터를 로딩하도록 설정하여 불필요한 로딩을 방지할 수 있다. - N+1 문제가 발생하는 지점에서 적절히 Fetch 전략을 조정하여 불필요한 조회를 줄일 수 있다.