JPA의 N+1 문제

Kkd·2024년 11월 20일
0

매일메일 개념정리

목록 보기
1/93

JPA의 N + 1 문제란?

N + 1 문제는 JPA 또는 ORM 사용 시, 연관된 엔티티를 조회할 때 발생하는 성능 문제입니다.
이는 하나의 쿼리로 대상 엔티티(1)를 조회한 뒤, 각 엔티티와 연관된 엔티티(N)를 조회하기 위해 추가 쿼리(N번)가 실행되면서 발생합니다.


문제 발생 과정

1. 상황

  • MemberTeam 엔티티가 @ManyToOne 관계로 매핑되어 있다고 가정합니다.
@Entity
public class Member {
    @Id @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY) // 지연 로딩 설정
    private Team team;
}

@Entity
public class Team {
    @Id @GeneratedValue
    private Long id;

    private String name;
}

2. 쿼리 예제

  • Member 엔티티를 전체 조회하고 각 Member가 속한 Team의 이름을 출력한다고 가정합니다.
List<Member> members = em.createQuery("SELECT m FROM Member m", Member.class)
                         .getResultList();

for (Member member : members) {
    System.out.println(member.getTeam().getName());
}

3. 실행 과정

  • SELECT m FROM Member m 쿼리: Member를 조회하는 쿼리가 1번 실행됩니다.
  • getTeam().getName() 호출 시: 각 MemberTeam을 조회하기 위해 Team 테이블에 대한 쿼리가 N번 실행됩니다.

문제점

  • 데이터베이스에서 불필요하게 많은 쿼리가 실행되어 성능이 저하됩니다.
    • 예를 들어, Member가 100명이라면, 1 + 100 = 101개의 쿼리가 실행됩니다.

해결 방법

1. 페치 조인(Fetch Join) 사용

  • JPQL에서 FETCH JOIN을 사용하면 연관된 엔티티를 한 번에 조회할 수 있습니다.
List<Member> members = em.createQuery(
    "SELECT m FROM Member m JOIN FETCH m.team", Member.class)
    .getResultList();
  • 결과적으로 MemberTeam을 한 번의 쿼리로 가져옵니다.

2. EntityGraph 사용

  • JPA의 @EntityGraph를 활용하면 특정 연관 필드를 조회 시 함께 가져오도록 설정할 수 있습니다.
@Entity
public class Member {
    @ManyToOne(fetch = FetchType.LAZY)
    @EntityGraph(attributePaths = "team")
    private Team team;
}
  • Repository 레벨에서 간단히 적용 가능합니다.

3. Batch Size 설정

  • @BatchSize를 통해 연관 엔티티를 묶어서 조회하도록 설정할 수 있습니다.
@BatchSize(size = 10)
@ManyToOne(fetch = FetchType.LAZY)
private Team team;
  • 또는 글로벌 설정:
spring.jpa.properties.hibernate.default_batch_fetch_size=10
  • 100명의 Member를 조회하면 Team을 10개씩 나누어 총 10번의 쿼리로 조회합니다.

결론

JPA의 N + 1 문제는 JPA를 사용하는 개발자라면 반드시 이해하고 해결해야 하는 주요 성능 문제입니다.
문제의 원인을 이해하고, 페치 조인, 엔티티 그래프, 배치 사이즈 설정 등을 적절히 활용하여 최적화하는 것이 중요합니다.

추가 학습 자료

profile
🌱

0개의 댓글