보충 학습 후 재정비가 필요한 글입니다.
N + 1 문제란
요청이 1개의 쿼리로 처리 되길 기대했는데, N개의 추가 쿼리가 발생하는 현상이다. N + 1 문제는 즉시 로딩과 지연 로딩 전략 각각의 상황에서 발생할 수 있다.
즉시 로딩에서 N + 1 이 생기는 이유
JPQL이란 Java Persistence Query Language의 약어로, 엔티티 객체를 대상으로 쿼리를 작성할 수 있도록 해준다.
즉시 로딩에서 발생하는 이유는 JPQL을 사용하는 경우 전체 조회를 했을 때, 영속성 콘텍스트가 아닌 데이터베이스에서 직접 데이터를 조회한 다음 즉시 로딩 전략이 동작하기 때문이다.
다시 말하자면, JPQL은 즉시 로딩 쿼리를 만들때, 연관관계가 있는 엔티티는 신경 쓰지 않고, 조회 대상이 되는 엔티티를 기준으로 쿼리를 만든다. 따라서, 처음에 조회 대상 엔티티 기준의 쿼리를 날리고, 연관된 엔티티가 있음을 확인한 이후에 글로벌 패치 전략을 확인한다. (이때, N번의 쿼리 발생)
즉시 로딩에서 N + 1 해결하는 방법
즉시 로딩을 최대한 사용하지 말고, 지연 로딩 + fetch join을 사용하자.
지연 로딩에서 N + 1 이 생기는 이유
지연 로딩에서 발생하는 이유는 지연로딩 전략을 사용한 하위 엔티티를 로드할 때, JPA에서 프록시 엔티티를 unproxy 할 때 해당 엔티티를 조회하기 위한 추가적인 쿼리가 실행되어 발생한다. (하위 엔티티가 1차 캐시 저장소에 없는 경우)
지연 로딩에서 N + 1 해결하는 방법
Fetch join을 이용해서 해결할 수 있다. Fetch join은 연관된 엔티티나 컬렉션을 한 번에 같이 조회하는 기능이다. 즉, 연관된 엔티티까지 영속성 콘텍스트에 전부 올려버린다.
단, 이 방법은 불필요한 쿼리문이 추가되는 단점이 있다. 이 필드는 Eager 조회, 저 필드는 Lazy 조회를 해야한다까지 쿼리에서 표현하는 것은 불필요하다라고 생각할 수 있다. 이러한 문제를 해결하기 위해서는 @EntityGraph를 이용할 수 있다.
Fetch join 주의 사항
데이터를 데이터베이스에서 읽어와 화면에 출력할 때 한꺼번에 모든 데이터를 가져오는 것이 아닌
출력될 페이지의 데이터만 나눠서 가져오는 것을 페이징(Paging)이라고 한다.
OneToMany에서 페이징 처리를 할 때, 데이터 누락이 생길 수 있다.
아래와 같은 상황에서 offset 1 ~ limit 5면 데이터 누락이 발생된다.
user_id | user_name | hobby | id |
1 | 또치 | 하키 | 1 |
1 | 또치 | 럭비 | 2 |
1 | 또치 | 축구 | 3 |
1 | 또치 | 야구 | 4 |
2 | 둘리 | 농구 | 5 |
JPA는 위와 같은 데이터 누락을 해결하기 위해서 데이터를 전체 full scan 해서 가져오고, 메모리에서 페이지 처리를 한다. 즉, 메모리 부하가 생길 수 있다.
이러한 문제의 해결책으로 ManyToOne일 때, fetch join을 사용하거나, @BatchSize를 사용할 수 있다.
'개발(레거시) > JPA' 카테고리의 다른 글
JPA 1대N 관계 양방향 매핑 상황별 쿼리 갯수 예측 (0) | 2022.12.02 |
---|