우아한테크코스 테코톡

수달의 JPA N+1 문제

https://youtu.be/ni92wUkAmQI

수달의 JPA N+1 문제

JPA 에서 N+1 문제란?

  • 요청이 1개의 쿼리로 처리 되길 기대 했는데 N개의 추가 쿼리가 발생하는 현상

지연(Lazy) 로딩 이란?

  • 지연 로딩은 엔티티를 조회할 시 사용할 때까지 데이터 로딩을 미루는 현상을 말한다.
  • JPQL 이란?
    • JAVA Persistence Query Language 엔티티를 대상으로 쿼리 작성
  • 지연 로딩 설정 후 필요한 시점에 해당 객체들을 반복문을 돌면서 조회시 1차 캐시에 데이터가 없어서 쿼리가 반복적으로 나간다. 이런 문제를 N+1 문제라고 한다.
  • 해결 방법 : Fetch join으로 해결할 수 있다.
    • 연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 기능
      • 연관된 엔티티까지 영속성 컨텍스트에 전부 올려버린다.
    • 사용 방법
      • 쿼리문에 직접 fetch를 명시하는 방법
        • img.png
      • @EntityGraph라는 어노테이션을 사용해서 가져오는 방법
  • 최초에 관련되 데이터를 한꺼번에 가져와서 객체화 해줬기 때문에 DB를 거치지 않고 데이터를 꺼내서 반환 한다, 즉 1개의 쿼리로 문제 해결된다.
  • 지연로딩 하지말고 즉시 로딩으로 가져오면 N+1 문제가 없지 않을까?
    • 즉시 로딩일 때도 N+1 문제가 발생한다.

즉시(Eager) 로딩이란?

  • JPQL이 findAll을 했을 때 JPQL 자체가 엔티티 기준으로 쿼리를 만들어 준다고 했다 그래서 처음 쿼리를 만들 때 연관관계가 있는 엔티티는 신경 안쓰고 조회 대상이 되는 Entity 기준으로만 쿼리를 만든다.
  • 해결 방법
    • 즉시 로딩을 최대한 사용하지 말고, 지연로딩 + fetch join 사용

그럼 N+1은 Fetch join 이면 다 해결되네요?

  • Fetch join으로 N+1 문제는 해결했지만 이걸 사용함으로써 발생하는 side effect가 있다.
  • 대표적인 Fetch join 문제 상황
    • OneToMany 관계에서 페이징 처리할 때
      • 예상했던 결과랑 다름
      • 데이터 누락 문제
    • 이런 문제들을 JPA가 알아서 fetch join이랑 페이징을 같이 하게 되면은 fetch join한 데이터를 일단 전부 다 가져와서 인메모리(RAM, Heap Area)에 넣고 가공하는 작업을 거치게 된다.
      • 문제 인 이유
        • 쿼리문으로 Limit가 없다.
        • 데이터 전체 full scan 해서 가져오고, 메모리에서 페이지 처리한다.
    • 해결 방법으로 @ManyToOne일 때 페이징 처리를 해라 혹은 @BatchSize()를 사용하는 해별 방법이 있다.

© 2020. All rights reserved.

SIKSIK