N+1?
- 연관된 엔티티를 로드할 때 발생한다.
- 한 개의 쿼리로 N 개의 엔티티를 로드 후
- 각 엔티티와 연관된 다른 엔티티들을 로드하기 위해
- 추가적으로 N 번의 쿼리를 수행하게 됨.
- 최종적으로 N + 1 의 호출이 발생하는 것이다.
예제로 알아보자
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
private List<Order> orders = new ArrayList<Order>();
}
@Entity
@Table(name = "ORDERS")
public class Order {
@Id @GeneratedValue
private Long id;
@ManyToOne
private Member member;
}
Member
엔티티 목록을 조회시,- 각
Member
엔티티마다 연관된Order
엔티티들을 가져오기 위한 별도의 쿼리가 수행된다.
- 만약 10개의
Member
엔티티를 조회하면- 최초의 1번 쿼리 ( 10 개의
Member
를 가져오는 쿼리 )
- 각
Member
에 대한 10번의 추가 쿼리가 수행되어
- 총 11번의 쿼리가 수행됨.
- 최초의 1번 쿼리 ( 10 개의
- 각
- 한번 테스트코드로 확인해볼까?
@Test public void nPlusOneProblemTest() { // Member 엔티티를 조회합니다. List<Member> members = memberRepository.findAll(); for (Member member : members) { // 각 Member 엔티티에 연관된 Order 엔티티를 조회합니다. List<Order> orders = member.getOrders(); for (Order order : orders) { System.out.println("Order id: " + order.getId()); } } }
- 단일 회원 조회의 경우
Member
엔티티와 연관된Order
컬렉션이 즉시 로딩으로 설정되어있기에, 이를 함계 조회
- Join 으로 한번에 들고오긴함
- 다중 회원 조회
select m1_0.id,m1_0.name from member m1_0;
- 일단 모든 멤버를 들고오긴한다.
- 하지만..
- 회원의 수만큼
- 조인된 쿼리가 발사된다.
select o1_0.member_id,o1_0.id from orders o1_0 where o1_0.member_id=1; select o1_0.member_id,o1_0.id from orders o1_0 where o1_0.member_id=2;
왜 발생할까?
- ORM 의 동작방식에 집중해야함
- 초기 쿼리 수행 문제
처음에는 Member 엔티티라면 ..연관된 엔티티를 고려치않고 주 엔티티만을 가져온다.
→ 이게 주요한것 같다.
- 즉시 로딩
- 초기 쿼리가 수행되는 즉시
- 연관된 엔티티를 조인하여 들고올수있도록 다시 하나의 레코드에 대해 쿼리를 친다.
- 단건의 레코드마다 → 조인한 쿼리가 발사된다.
- 단일 회원 조회의 경우
단점은?
그냥 딱봐도 문제다 SQL 이 몇번 실행되는건지..
만약 100명의 사용자가 회원 100명에 대한 쿼리만 쳐도 101개 * 100 ⇒ 10100 회의 SQL 이 날아간다.
이건 지옥이다.
해결방안
- 지연 로딩의 사용
- FetchType.LAZY 를 통해
- 필요한 시점에 관련 데이터를 로드해야한다
- 하지만 지연로딩도 N+1 에서 아예 자유로울순없다. 후술하겠다. → 어차피 연관 엔티티에 접근을 하는 경우 계속적으로 쿼리가 발생한다.
- FetchType.LAZY 를 통해
- 조인 페치 사용
- JPQL 에서
JOIN FETCH
를 통해 필요한 데이터를 들고와야한다.
- JPQL 에서
배치 사이즈 조절
← 추천- 하이버네이트 JPA 구현체에서 배치 사이즈 옵션을 통해 한 번에 쿼리에 몇개의 연관 객체를 같이 들고올 것인지 설정이 가능하다.
- 만약 주문 1억개고 배치 사이즈가 1천개라면
- 초기쿼리 + 1천개의 상품 가져오는 쿼리 + …반복 된다.
Uploaded by N2T
'자바 > JPA' 카테고리의 다른 글
[JPA] 영속성 컨텍스트가 다를때의 비교 (0) | 2023.11.19 |
---|---|
[JPA] 프록시의 동등성 비교법 (0) | 2023.11.19 |
[Jpa] @Convertor 사용 (0) | 2023.11.19 |
[Jpa] 트랜잭션 범위와 영속성 컨텍스트 (0) | 2023.11.17 |
[Jpa] 준영속과 지연 로딩 (0) | 2023.11.17 |