본문 바로가기

Server/Spring

[날카로운 질문하는 머쓱이] N+1 문제

반응형
SMALL

오늘의 질문 빠밤 >~< N+1 문제 ..? 처음 들어봤다!

 

N+1 문제

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

 

예를 들어, 주문 내역에서 구매 상품(연관 관계가 설정된 엔티티)들을 조회할 때, 구매 상품 수만큼 조회 쿼리가 발생한다.

 

발생 이유

JPA는 메서드 이름을 분석하고 JPQL을 생성하여 실행된다.(JPQL은 SQL을 추상화 한 객체지향 쿼리 언어로써 특정 SQL에 종속되지 않고 엔티티 객체와 필드 이름을 가지고 쿼리를 실행한다.) 조회 쿼리(예를 들면 findAll())를 날리면 연관 관계를 무시하고, 해당 엔티티만을 조회하는 쿼리를 실행한다. 그렇기 때문에 연관된 엔티티 데이터를 조회하기 위해서, 관련 엔티티 조회를 별도로 호출한다.

 

해결 방안

Fetch Join

- @Query로 최적화 된 쿼리를 직접 작성한다. (예를 들면 @Query("SELECT o FROM orders o JOIN FETCH o.products"))

- (단점) 데이터 호출 지점에 모든 연관 관계의 데이터를 가져오기 때문에 FetchType.LAZY가 무의미 해진다.

 

Entity Graph

@EntityGraph(attributePaths = "products")
@Query("select o from orders")

- @Query에 특정 엔티티 조회 쿼리문을 작성하고, @EntityGraph에 연관 관계 엔티티를 설정한다.

- OUTER JOIN으로 실행된다.

 

단, Fetch Join과 Entity Graph 모두 join을 사용하기 때문에 데이터 중복이 발생할 수 있다. 이는 아래 2가지 방법으로 해결 가능하다.

- 컬렉션을 Set으로 사용하여 중복 제거

- 쿼리에 DISTINCT를 사용하여 중복 제거

 

SUBSELECT

- 해당 엔티티를 조회하는 쿼리는 그대로 발생하고, 연관 관계의 데이터를 조회할 때 서브 쿼리로 함께 조회한다.

- 2번의 쿼리 실행

 

BatchSize

- 연관된 엔티티를 조회할 때, 정해진 size만큼만 조회한다. (SQL IN절 사용)

 

Query Builder

return from(orders).leftJoin(orders.products, products)
                  .fetchJoin()

- 위와 같이 지원해주는 다양한 플러그인을 사용하여 쿼리를 실행한다.

 

 

 

References

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

 

N+1 문제 - Incheol's TECH BLOG

Query를 실행하도록 지원해주는 다양한 플러그인이 있다. 대표적으로 Mybatis, QueryDSL, JOOQ, JDBC Template 등이 있을 것이다. 이를 사용하면 로직에 최적화된 쿼리를 구현할 수 있다.

incheol-jung.gitbook.io

 

반응형
LIST