본문 바로가기

Backend/Spring

Spring JPA - N+1 문제 정리

1.  N+1 문제란 ? 

연관 관계에서 발생하는 이슈연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 갯수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오게 된다.

 

- 예시

연관관계 예시

위와같이 ORDERS MEMBER 가 n:1매핑, DELIVERY와 1:1매핑이라고 가정했을 때

// 엔티티를 DTO로 변환
public List<SimpleOrderDto>ordersV2(){
	List<Order> orders= orderRepository.findAll();  // 초기 1번 조회
	List<SimpleOrderDto>result = orders.stream()    //order -> member 지연 로딩 조회 N번 + order -> delivery 지연 로딩 조회 N번
				.map(o -> new SimpleOrderDto(o)) //dto에서 name = order.getmember().getName(); 같은 코드 실행 시, 조회됨
				.collect(toList());
}

지연로딩으로 인해 바로 member 와 delivery를 가져오는게 아니라 SimpleOrderDto에서 아래와 같은 코드와 같은 접근을 하여 값을 가져올 때 조회를 하게 된다.

//SimpleOrderDto
name = order.getmember().getName();

따라서, 초기 1번 조회 + order -> member N번 조회 + order -> delivery N번 조회를 총 1+2N번의 조회를 일으킬 수 있는데, 이와 같이 연관관계로 인한 N번의 추가 조회 문제를 N+1문제라고 한다.

 

이 1+N 문제는 연관관계와 조회가 많아질 수록 성능에 있어서 큰 악영향을 미친다.

 

2. 페치 조인 사용하기

1+N문제를 해결하기 위해 지연로딩을 즉시 로딩으로 바꾸는 것은 다른 문제를 일으키기 쉽다.

따라서 가급적이면 페치 조인을 사용해 이를 해결한다.

 

페치조인이란?
JPA에서 연관된 엔티티를 한 번의 쿼리로 모두 가져올 수 있는 조인
SQL을 확인해보면 단순히 inner join 쿼리가 나가는 것을 볼 수 있다.
// JPQL
select m from Memeber m join fetch m.team

// 실제 데이터베이스에 전송되는 SQL
SELECT M.*, T.*FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID = T.ID

해당 방식을 사용하면 위와 같은 1 + N + N 번의 쿼리를 1번에 조회할 수 있다.(성능 최적화 최고)

페치 조인으로 order → member , order → delivery가 이미 조회된 상태이므로 지연 로딩 자체가 일어나지 않는다.

 

  • 즉시 로딩과의 차이점
    • 즉시로딩 전략은 묵시적인 방식이라서 원하지 않는 타이밍에도 항상 연관필드를 조인을 하고 결과를 반환한다.
    • 페치조인은 명시적으로 동적인 타이밍에 원하는 객체그래프를 탐색할 수 있어 유용하다.

 

 

3. 페치 조인 최적화하기

//Repository
em.createQuery("select o from Order o" +
        " join fetch o.member m" +
        " join fetch o.delivery d" +
        " join fetch o.orderItems oi" +
        " join fetch oi.item i", Order.class)
    .getResultList();

inner join을 생각해보면 이해하기 쉬운데, 페치 조인을 많이 사용하면 같은 orderId이지만 다른 속성값들 때문에 여러 row 조회된다.

⇒ distinct로 중복을 해결하면 최적화할 수 있다.

 

 

4. 페치 조인의 장단점

장점 : 쿼리 호출 수 1 + N ⇒ 1+ 1로 최적화 가능

단점 : 컬렉션 페치 조인 사용시 페이징 불가(ToOne 관계에서는 해당하지 않음)

⇒ batch_fetch_size로 해결 가능 : 설정한 사이즈 만큼 IN 쿼리로 조회

 

 

 

 

 


해당 포스트는 김영한님의 실전! 스프링 부트와 JPA 활용1 을 듣고, 정리한 글입니다.

 

 

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 인프런 | 강의

실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니다., - 강

www.inflearn.com

'Backend > Spring' 카테고리의 다른 글

스프링 보일러 플레이트 Docker EC2배포하기 A to Z  (0) 2022.08.31