우아한테크코스 테코톡
수달의 JPA N+1 문제
카테고리 : 우아한테크코스 테코톡
수달의 JPA N+1 문제
JPA 에서 N+1 문제란?
- 요청이 1개의 쿼리로 처리 되길 기대 했는데 N개의 추가 쿼리가 발생하는 현상
지연(Lazy) 로딩 이란?
- 지연 로딩은 엔티티를 조회할 시 사용할 때까지 데이터 로딩을 미루는 현상을 말한다.
- JPQL 이란?
- JAVA Persistence Query Language 엔티티를 대상으로 쿼리 작성
- 지연 로딩 설정 후 필요한 시점에 해당 객체들을 반복문을 돌면서 조회시 1차 캐시에 데이터가 없어서 쿼리가 반복적으로 나간다. 이런 문제를 N+1 문제라고 한다.
- 해결 방법 : Fetch join으로 해결할 수 있다.
- 연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 기능
- 연관된 엔티티까지 영속성 컨텍스트에 전부 올려버린다.
- 사용 방법
- 쿼리문에 직접 fetch를 명시하는 방법
- @EntityGraph라는 어노테이션을 사용해서 가져오는 방법
- 쿼리문에 직접 fetch를 명시하는 방법
- 연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 기능
- 최초에 관련되 데이터를 한꺼번에 가져와서 객체화 해줬기 때문에 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()를 사용하는 해별 방법이 있다.
- OneToMany 관계에서 페이징 처리할 때