eager fetch, lazy fetch, N+1문제에 대해서 알아보자
먼저 Fecth란?
애플리케이션이 DB로 부터 데이터를 가져오는것
DB와 통신하여 데이터를 읽는 것에는 큰 비용이 소모되기 때문에, 똑똑하게 가져오는 전략이 필요하다
-
eager: 프로그램 코드가 쿼리를 날리는 시점에 데이터를 즉시 가져오기
- ex: select a.id from A a inner join B b on a.b_id=b_id
(A,B를 조인을하지만 A만봄,
b를 보지 않았지만 일단 다 가져옴)
-
lazy: 가져오려는 데이터를 애플리케이션에서 실제로 접근할 때 가져오기
- ex: select a.id from A; (select b from B b where b.id = ?)
- 가져온 a.id를 이용해 b를 구하려고할때 a.id를 사용하면 비용도 적게드는 장점이 있다.
-
lazy 전략은 기본적으로 ORM의 특징이자 기능적 장점을 가지고 있다
- 더 빠르고 경제적인 쿼리(적절히 사용한다면)
- 잘못 사용하면 데이터 접근 에러(ex:LazyInitializationExcetpion)
fecth 기본적략(default setting)
각 JPA 연관관계 애노테이션은 기본 fetch 전략을 가지고이 있다.
기본 세팅의 핵심은 어느쪽이 효율적인가? 이다.
-
@OneToOne : FetchType.EAGER
-
@ManyToOne : FetchType.EAGER
-
@OneToMany :FetchType.LAZY
-
@ManyToMany : FetchType.LAZY
fetch 전략의 설정(실전)
효율성- 데이터가 어느쪽으로 더 자주 사용될 것같은가를 예측해야한다.
-
default 내버려두기: 필요한 시점에 최선의 방식으로 데이터를 가져온다.
-
LAZY 사용: 연관관계가 있는 엔티티에서 자식 엔티티만 가져오는 시나리오일 때
- 프로그래머가 로직 흐름에서 join을 의식하고 있지 않을때 LAZY사용이 적절하다.
- LAZY 세팅이 후속 쿼리 발생 방지를 꼭 보장하고 있지 않음
- ex: 불러들인 자식 엔티티가 서비스 레이어 어딘가에서 결국 부모 엔티티 필드를 건드렸을 경우
-
EAGER 사용: 연관관계가 있는 엔티티에서 무조건 다 가져오는 시나리오일때
- 프로그래머가 join을 사용해야하는 상황임을 인지하고 있음
- EAGER 세팅이 join동작을 보장하지는 않음
- ex: Spring Data JPA 쿼리 메소드 findAll()
- JPQL을 직접작성해서 join을 영속성 컨텍스트에 알려줘야함(대표적인 방식: queryDsl)
N+1 query 문제
쿼리를 한번만 날렸을 뿐인데 1+N개의 쿼리가 더생기는 오류 or 현상
N+1 query problem 해결방법
3가지방법
- 똑똑한 lazy
- 비지니스 로직을 면밀히 분석하여, 불필요한 연관관계 테이블 정보를 불러오는 부분을 제거한다.
- 가장 똑똑하고 효율적인방법
- eager fetch + join jpql
- eager fetch로 설정해서 join쿼리를 이용해서 데이터를 받아오거나 보낸다.
- join 쿼리를 직접 작성하는 방법은 다양(@Query,querydsl,..)
- 쿼리 한 번에 오긴 하겠지만, join쿼리 연산 비용과 네트워크로 전달되는 데이터가 클 수 있음
- 후속 쿼리를 in으로 묶어주기 : N+1-> 1+1로 I/O를 줄일 수 있음
- (Join사용이 어렵거나 복잡할떄 사용하는 방법)
- 하이버네이트 프로퍼티: default_batch_fetch_size
- 스프링부트에서 쓰는법: spring.jpa.properties.hibernate.default_batch_fetch_size
- 100~1000사이를 추천
- 모든 쿼리에 적용되고, 복잡한 도메인에서 join쿼리를 구성하는 것이 골치아플 때 효율적
순환 참조 문제
각 모듈이 서로를 의존하고 있는 상태
스프링을 사용하다보면,JPA뿐만아니라 다양한 위치에서 간혹 경험하게 됨
- 스프링 컴포넌트끼리 참조하는 경우
- JPA에서 가장 흔하게 발생하는 순환 참조: toString()(lombok)을 사용했다가 발생하는 순환 참조 문제
해결방법은 한쪽의 참조를 제거하는것으로 간단히 해결이 된다.