카테고리 없음

n+1 쿼리문제 및 fetch

티에리앙리 2022. 4. 4. 00:49

n+1 쿼리문제
쿼리 1개로 n개의 쿼리가 더 나가는 문제

연관 관계에서 발생하는 이슈로 
연관 관계가 설정된 엔티티를 조회할 경우에 조회된 데이터 갯수(n) 만큼 연관관계의 조회 쿼리가 추가로 발생하여 데이터를 읽어오게 된다. 이를 N+1 문제라고 한다. 

 

일단 기존에

우리 코드에서

default가 FetchType.EAGER 라서

이미 영속화된 USER는 SELECT로 불러올 필요 없고 1차 캐시에서 가져오기만 하면 되니까 SELECT 할 필요가 없다.
EGGER 연관관계에 있는거 다불러온다.

 

즉,

EAGER(즉시로딩) : 연관된 엔티티를 즉시 조회

LAZY(지연로딩) : 사용할 때만 연관된 엔티티를 조회 

 

연관관계라 FetchType.EAGER 로 돼있는 엔티티를 불러오면

그 엔티티의 연관관계를 또 불러온다. (N+1쿼리 문제가 발생한다.

LAZY 이면 findAll만 하면 N+1 문제 발생 안하지만, LAZY를 사용하면

 

PostLikes 클래스안에 있는 User

@ManyToOne(fetch = FetchType.LAZY) // ManyToOne의 default는 EAGER,
@JoinColumn(name = "user_id", nullable = false)
private User user;

postLikes.getUser()할 때 n+1 문제 발생한다.

 

이를 해결 하려면

paging 안하면 fetch join하고, paging 하면 배치사이즈를 사용한다.

 

fetch join 은

  • 기존 SQL의 조인이 아님
  • JPQL의 성능 튜닝을 위해 제공되는 조인
  • 연관된 엔티티 or  컬렉션을 SQL 한번에 함께 조회하는 기능

SQL에서 사용하는 조인의 종류는 아니고 JPQL에서 성능 최적화를 위해 제공하는 기능입니다.
이것은 연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 기능인데 join fetch 명령어로 사용할 수 있습니다.

 

이런 페치 조인을 사용하면 SQL 한 번으로 연관된 엔티티들을 함께 조회할 수 있어서 SQL 호출 횟수를 줄여 성능을 최적화할 수 있습니다.

 

 

fetch join 쓰는 것보다 그냥
default_batch_fetch_size: n 사용하는게 
그냥 select  정보 한두번 불러오는게 
fetch join으로 하는 것보다 실행시간이 괜찮은 듯?

 

이 batch-size 옵션은 하위 엔티티를 로딩할 때 한 번에 상위 엔티티 ID를 지정한 숫자만큼 in Query로 로딩해 줍니다.

현재 프로젝트를 예로 들면 batch-size:100으로 되어있으면, 상위 엔티티인 Paragrah의 id 100 개를 in Query로 User 와 Paragraph 를 조회하게 됩니다.