JPA N+1 문제

연관 관계에서 발생하는 이슈로, 연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 갯수(N) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오게 된다. 이로 인해 많은 량의 쿼리가 발생하며 성능 저하가 발생한다. @ManyToOne 연관관계를 가진 엔티티에서 주로 발생한다.

fetchType이 Eager이든 Lazy든 발생할 수 있다.

  • 즉시 로딩으로 데이터를 가져오는 경우.
  • 지연 로딩으로 데이터를 가져온 이후에 가져온 데이터에서 하위 엔티티를 다시 조회하는 경우.

N+1 문제가 바로 발생하느냐, 아니면 하위 엔티티를 조회하는 시점에 발생하느냐의 차이이다.

 

해결 방안

  1. fetch join
    • 조회 시 바로 가져오고 싶은 Entity 필드를 지정하는 것이다.
    • 한번의 쿼리만이 발생하도록 설계할 수 있다.
    • fetch join를 여러 번 사용하여 특정 엔티티의 하위 엔티티의 하위 엔티티까지 가져오도록 할 수 있다.
    • inner join 발생
    • 단점
      • 불필요한 쿼리문이 추가된다.
      • 페이징 쿼리를 사용할 수 없다. 하나의 쿼리문으로 가져오다 보니 페이징 단위로 데이터를 가져오는것이 불가능하다.
      • 연관 관계가 여러개인 엔티티를 동시에 fetch join 할 수 없다.

 

  1. @EntityGraph
    • @EntityGraphattributePaths에 쿼리 수행시 바로 가져올 필드명을 지정하는 방법이다.
    • outer join 발생
    • 원본 쿼리의 손상 없이 사용할 수 있다.

fetch join과 @EntityGraph방식 모두 공통적으로 카테시안 곱(Cartesian Product)이 발생하여 상위 엔티티의 수만큼 하위 엔티티의 데이터 중복이 존재할 수 있다. 이는 Set을 사용하거나 distinct를 사용하여 해결할 수 있다.

 

  • QueryBuilder를 함께 사용하자
    • 간단한 구현은 JPA를 사용하면 개발 성능을 향상시킬 수 있지만 만능은 아니다.
    • JPA 만으로는 실제 복잡한 비즈니스 로직을 모두 구현하기 어려울 수 있다.
    • 불필요한 쿼리도 늘 조심해야한다.
    • 그렇기에 QueryBuilder(예: Querydsl)를 함께 사용하자.

 

출처

https://velog.io/@woo00oo/N-1-%EB%AC%B8%EC%A0%9C

https://incheol-jung.gitbook.io/docs/q-and-a/spring/n+1#n+1

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기