BE/Java

[JPA] N + 1 문제

baek-dev 2025. 1. 6. 18:51

데이터베이스와 관련된 성능 이슈로, JPA나 ORM을 사용할때 자주 발생한다

한번의 쿼리로 해결할 수 있는 작업이 여러번의 쿼리로 나뉘어 실행되면서 성능 저하를 유발하는 문제

 

동작 과정

1. 1개의 쿼리 실행

  • 먼저 1 에 해당하는 쿼리를 실행해 특정 엔티티의 리스트를 가져온다
  • 예 : SELECT * FROM orders; (주문 데이터를 조회)

2. N개의 추가 쿼리 실행

  • 가져온 각 엔티티와 연관된 데이터를 조회하기 위해 N개의 추가 쿼리가 실행된다
  • 예 : 각 주문에 연관된 고객 데이터를 가져오는 쿼리

예제 및 동작 설명

Order 와 Customer 관계 (@ManyToOne) :

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

    @ManyToOne(fetch = FetchType.LAZY) // 연관관계 설정
    private Customer customer;
}

 

문제 발생시 동작 :

List<Order> orders = orderRepository.findAll(); // 모든 주문 조회
for (Order order : orders) {
    System.out.println(order.getCustomer().getName()); // 각 주문의 고객 이름 출력
}

 

1. 첫번째 쿼리 :

SELECT * FROM orders;
  • 모든 주문 데이터를 조회

 

2. 추가 N개의 쿼리 :

  • 각 주문마다 고객 데이터를 조회하기 위해 실행
SELECT * FROM customer WHERE id = ?; -- 주문 1의 고객
SELECT * FROM customer WHERE id = ?; -- 주문 2의 고객
...

N + 1 문제의 발생 원인

  • JPA의 기본 동작 방식 중 하나인 지연 로딩 (Lazy Loading)이 원인
  • 연관된 데이터를 사용하는 시점에 추가 쿼리를 실행하기 때문에 발생

해결방법

1. 즉시 로딩(Eager) 사용

  • 연관된 데이터를 한번의 쿼리로 가져옴
  • 연관 필드에 fetch = FetchType.EAGER 설정
@ManyToOne(fetch = FetchType.EAGER)
private Customer customer;

 

실행 쿼리 :

SELECT o.*, c.* 
FROM orders o 
JOIN customer c ON o.customer_id = c.id;

 

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

  • JPQL 에서 JOIN FETCH를 사용해 연관된 엔티티를 한번에 조회
@Query("SELECT o FROM Order o JOIN FETCH o.customer")
List<Order> findAllWithCustomer();

 

실행 쿼리 :

SELECT o.*, c.*
FROM orders o
JOIN customer c ON o.customer_id = c.id;

 

3. EntityGraph 사용

  • 엔티티 그래프를 사용해 특정 필드를 페치 조인 하도록 설정
@EntityGraph(attributePaths = {"customer"})
@Query("SELECT o FROM Order o")
List<Order> findAllWithCustomer();

 

4. Batch Size 설정

  • @BatchSize 어노테이션이나 hibernate.default_batch_fetch_size 설정을 통해 한번에 여러 데이터를 로드
@BatchSize(size = 10)
@ManyToOne(fetch = FetchType.LAZY)
private Customer customer;

문제 해결 시 주의사항

1. Eager Loading 과잉 사용 금지

  • 모든 연관 데이터를 즉시 로딩하면 불필요한 데이터를 가져와 성능 저하를 유발할 수 있음

2. 페치 조인 중복 방지

  • 여러 페치 조인을 사용할 경우, JPA에서 데이터 중복이 발생할 수 있음

3. 필요한 데이터만 조회

  • 항상 필요한 데이터만 정확히 가져오도록 쿼리를 설계

요약

  • N + 1 문제는 한 번의 쿼리로 처리 가능한 데이터를 여러 번의 쿼리로 가져와 성능 저하를 유발하는 문제
  • 주로 지연 로딩(Lazy Loading)으로 인해 발생
  • 해결 방법 :
    1. 즉시 로딩(Eager Loading)
    2. 페치 조인(Fetch Join)
    3. EntityGraph 사용
    4. Batch Size 설정
  • 최적화 시 필요 이상의 데이터를 불러오지 않도록 주의

 

 

 

 

출처 : ChatGPT

'BE > Java' 카테고리의 다른 글

[Java] HttpServletRequest  (0) 2025.01.08
[Java] Jackson 라이브러리  (0) 2025.01.07
[Java] Generic 제너릭  (0) 2025.01.04
[Java] enum  (0) 2025.01.01
[Java] @IdClass  (0) 2024.12.31