☄️ N+1 문제
N+1 문제란? JPA를 사용할 때 흔히 발생하는 문제 중 하나이다.
1번의 쿼리로 N개의 데이터를 가져왔는데, 그 데이터들을 처리하기 위해서 N번의 쿼리가 더 발생하는 문제이다.
🧐 언제 발생하는 문제일까?
몇몇 블로그에서 Fetch 전략에 따라 발생하는 문제라고 작성되어있다.
EAGER와 LAZY는 실제 데이터를 가져오는 시점의 차이이지 N+1 문제에 궁극적인 원인이 아니다.
JpaRepository에 정의한 메서드를 실행하면 JPA는 메서드 이름을 분석해서 JPQL 쿼리를 생성해 실행한다.
JPQL 객체지향 쿼리 언어로 테이블을 신경 쓰지 않고 엔티티와 필드만으로 쿼리를 생성한다.
따라서, N+1 문제는 JPQL이 연관관계를 무시하고 해당 엔티티만을 기준으로 쿼리를 조회하기 때문에 발생한다.
🛠️ 문제 해결하기
1️⃣ Join Fetch
쿼리문에 "join fetch"를 붙여 쿼리를 최적화해서 N+1 문제를 해결한다.
JpaRepository에서 자동으로 붙일 수 없으니 JPQL로 작성해야 한다.
Join Fetch는 Inner Join을 사용한다.
2️⃣ @EntityGraph
어노테이션의 attributePaths 속성에 가져올 필드명을 지정해서 N+1 문제를 해결한다.
Join Fetch와 동일하게 JPQL로 작성해야 하고 원본 쿼리를 건드리지 않고 N+1 문제를 해결할 수 있다.
@EntityGraph는 Outer Join을 사용하고 FetchType.EAGER을 사용한다.
😮 이렇게 쉽다고?
Join Fetch와 @EntityGraph를 사용할 때 주의점도 존재한다!
Join Fetch는 연관관계를 매핑할 때 설정한 FetchType이 의미가 없어진다라는 단점이 존재한다.
또한, 하나의 쿼리문을 사용해 페이징 단위로 데이터를 가져올 수 없다. (페이징 쿼리 사용불가)
두 방법 모두 카테시안 곱(Cartesian Product)이 발생하여 데이터가 중복 발생할 수 있다.
두 가지 방법으로 이 문제를 해결할 수 있다.
- Set 자료구조 사용하기 (데이터의 순서까지 고려한다면 LinkedHashSet)
- JPQL로 쿼리를 작성하니 distinct 사용하기 (Set보다 List가 맞다고 판단한 경우)
QueryBuilder를 사용하는 방식으로도 해결할 수 있으니 상황에 맞춰서 사용하면 되겠다!
🎇 Fetch 전략
JPA는 ORM이라는 객체와 테이블을 매핑시켜주는 기술로, 테이블의 연관관계는 객체의 참조로 이루어진다.
객체가 커질수록 참조는 많아지고, 많아진 만큼 데이터를 한 번에 불러오는 것은 데이터베이스 입장에서는 부담스럽다.
따라서 JPA는 Fetch 전략을 도입해서 데이터를 가져오는 시점을 변경해서 이런 부담을 줄이려고 한다!
1️⃣ 즉시 로딩, Eager
- 연관된 엔티티를 즉시 조회한다.
- 성능 최적화를 위해서 join을 사용한다.
- @ManyToOne과 @OneToOne의 default이다.
2️⃣ 지연 로딩, Lazy
- 연관된 엔티티를 프록시 객체로 바인딩한다.
- 프록시 객체가 실제로 사용될 때 쿼리문을 수행해 데이터를 가져온다.
- @ManyToOne과 @OneToOne의 default이다.
FetchType.EAGER는 예상치 못한 쿼리를 생성할 수도 있어서 일반적으로 FetchType.LAZY를 권장한다.
하지만 둘의 특성을 잘 파악하고 상황에 맞는 전략을 사용하자! 😋😋
-Reference :
https://jojoldu.tistory.com/165
https://incheol-jung.gitbook.io/docs/q-and-a/spring/n+1
😋 지극히 개인적인 블로그지만 훈수와 조언은 제 성장에 도움이 됩니다 😋
'개인 공부 > TIL' 카테고리의 다른 글
TIL : @PathVariable vs @RequestParam (20) (0) | 2023.01.10 |
---|---|
TIL : Random VS SecureRandom (19) (0) | 2022.12.20 |
TIL : StringUtils 사용하기 (17) (0) | 2022.12.01 |
TIL : JWT, Access Token / Refresh Token (16) (0) | 2022.11.30 |
TIL : 서버 인증 방식 (쿠키, 세션, 토큰) (15) (0) | 2022.11.30 |
댓글