거인의 코딩일지
[JPA] N+1 문제?? 본문
728x90
JPA의 N+1 문제란??
- 한번 조회해야 할 것을 N개의 종류의 데이터 각각을 추가로 조회하게 되서 총 N+1 번 DB를 조회하는 문제!
언제 발생하는가 ?
- JPA Repository 를 활용해 인테페이스 메소드를 호출할 시!
누가 발생시키는가 ?
- 1:N 또는 N:1 관계를 가진 엔티티를 조회할 때!
어떻게 발생하는가 ?
- JPA Fetch 전략(Fetch type) 이 EAGER 전략으로 데이터를 조회하는 경우
- JPA Fetch 전략(Fetch type) 이 LAZY 전략으로 전체 데이터를 가져온 이후 연관관계인 하위 엔티티를 사용할때 다시 조회하는 경우
왜 발생하는가?
- JPA Repository 로 find 할 때 실행하는 첫 쿼리에서 하위 엔티티까지 한번에 가져오지 않고 하위텐티티를 사용할 때 추가로 조회하기 때문이다
- JPQL 은 기본적으로 글로벌 Fetch 전략을 무시하고 JPQL 만 가지고 SQL 을 생성하기 때문이다.
EAGER(즉시로딩) 인 경우
- JPQL에서 만든 SQL 을 통해 데이터를 조회
- 이후 JPA에서 Fetch 전략을 가지고 (이땐 즉시로딩) 해당 데이터의 연관관계인 하위 엔티티들을 추가 조회(LAZY : 지연로딩)과정에서 N+1 문제가 발생한다.
LAZY(지연로딩)인 경우
- JPQL에서 만든 SQL 을 통해 데이터를 조회
- JPA 에서 Fetch 전략을 가지지만 여기서는 지연로딩이기에 추가조회는 하지 않는다
- 하지만 하위엔티티를 가지고 작업하게 되면 추가 조회하기에 결국 N+1 문제가 발생한다.
@ManyToOne 의 기본 FetchType 은 EAGER 이지만 특별한 경우가 아니라면 LAZY를 기본 세팅해서 사용하는 것이 옳다.
LAZY 를 사용하더라도 N+1 이 발생할수 있음을 인지하고 사용하여야 한다.
N+1 해결방법???
Fetch Join, BatchSize, EntityGraph 어노테이션 등의 방법이 있다.
- Fetch Join
- DB에서 데이터를 가져올 때 처음부터 연관된 데이터 까지 같이 가져오게 하는 것(SQL Join 문)
- 별도의 메소드를 만들어 줘야하며 @Query 어노테이션을 사용하여 "join fetch 엔티티. 연관관계 _ 엔티티" 구문을 만들어 주면 된다.
- 장점
- 단 한번의 쿼리만 발생하도록 설계할수있다.
- fetch join 을 이용해 특정 엔티티의 하위엔티티의 하위엔티티 까지 가져오도록 할 수 있다.
- 단점
- 번거롭게 쿼리문을 작성해야한다.
- JPA가 제공하는 Pageable 기능을 사용하지 못한다 => 페이징 단위로 데이터 가져오기 불가능
- batchsize 로 해결 : 즉시로딩이나 지연로딩시에 연관된 엔티티를 조회할 때 지정한 size 만큼 sql 의 IN 절을 사용하여 조회하는 방식
- 1: N 관계가 2개인 엔티티를 fetch join 불가능 => MulitipleVagfetch Exception 발생
- Batch Size
- 이 옵션은 정확히는 N+1 문제를 안일어나게 하는 방법은 아니고 N+1 문제가 발생하더라도 "Select * from user where team_id = ?" 가 아닌 "Select * from user where team_id in (?,?,?,?)" 방식으로 N+1 문제가 발생하게 하는 방법
- EntityGraph 어노테이션
- EntityGraph 상에 있는 Entity 들의 연관관계 속에서 필요한 엔티티와 컬렉션을 함께 조회하려고 할 때 사용
- outerJoin 사용
- attribute Paths에 쿼리수행시 바로 가져올 필드명을 지정하면 LAZY(지연로딩)가 아닌 Eager(즉시로딩) 조회로 가져오게된다.
- 장점
- fatch join의 매번 쿼리를 작성하고 확인하느 문제 수행
- 단점
- outerjoin 을 사용하기 때문에 중복데이터가 발생한다 (카테시안 곱 현상)
- Set 으로 중복 방지 / distinct 적용!
- outerjoin 을 사용하기 때문에 중복데이터가 발생한다 (카테시안 곱 현상)
![](https://t1.daumcdn.net/keditor/emoticon/friends1/large/016.gif)
728x90
'코딩 > JAVA' 카테고리의 다른 글
[Query_DSL] Query_DSL 이란??? (0) | 2023.09.25 |
---|---|
[JPA_Query DSL] BooleanBuilder, BooleanExpression 차이 (0) | 2023.09.25 |
[JPA] 영속성 컨텍스트란? (0) | 2023.08.24 |
선언적 트랜잭션? @Transctional? JPA? Proxy? (0) | 2023.08.24 |
[JPA] JPA(Java Persistenence API) 란????? (0) | 2023.08.22 |