2023. 6. 12. 17:14ㆍBackEnd(Java)/Spring Data JPA
✅ 아래 내용들에 대해서 알아보자
- 컬렉션 패치 조인
- 컬렉션 패치조인 문제점
- 컬렉션 패치조인 한계
- 컬렉션 패치조인 최적화
컬렉션 패치 조인
아래 코드를 보면 Order 엔티티와 OrderItem 엔티티가 N:1인 관계 OnetoMany로 구성되어 있고, Order와 orderItems를 join fetch로 컬렉션 패치 조인 하고 있는 것을 확인할 수 있다.
public List<Order> findAllWithItem() {
return em.createQuery("select distinct o from Order o"
+ " join fetch o.member m"
+ " join fetch o.delivery d"
+ " join fetch o.orderItems oi", Order.class)
.getResultList();
}
실제 실행 쿼리 값을 보면 inner join으로 쿼리가 수행되는 것을 확인할 수 있다.
select
distinct order0_.order_id as order_id1_6_0_,
member1_.member_id as member_i1_4_1_,
delivery2_.delivery_id as delivery1_2_2_,
orderitems3_.order_item_id as order_it1_5_3_,
order0_.delivery_id as delivery4_6_0_,
order0_.member_id as member_i5_6_0_,
order0_.order_date as order_da2_6_0_,
order0_.status as status3_6_0_,
member1_.city as city2_4_1_,
member1_.street as street3_4_1_,
member1_.zipcode as zipcode4_4_1_,
member1_.name as name5_4_1_,
delivery2_.city as city2_2_2_,
delivery2_.street as street3_2_2_,
delivery2_.zipcode as zipcode4_2_2_,
delivery2_.status as status5_2_2_,
orderitems3_.count as count2_5_3_,
orderitems3_.item_id as item_id4_5_3_,
orderitems3_.order_id as order_id5_5_3_,
orderitems3_.order_price as order_pr3_5_3_,
orderitems3_.order_id as order_id5_5_0__,
orderitems3_.order_item_id as order_it1_5_0__
from
orders order0_
inner join
member member1_
on order0_.member_id=member1_.member_id
inner join
delivery delivery2_
on order0_.delivery_id=delivery2_.delivery_id
inner join
order_item orderitems3_
on order0_.order_id=orderitems3_.order_id
컬렉션 패치 문제점
onetoMany 컬렉션 패치 조인 시 문제점이 있는데.. 바로 페이징 처리가 안된다는 문제점이 있다!!
public List<Order> findAllWithItem() {
return em.createQuery("select distinct o from Order o"
+ " join fetch o.member m"
+ " join fetch o.delivery d"
+ " join fetch o.orderItems oi", Order.class)
.setFirstResult(1)
.setMaxResults(100)
.getResultList();
}
위의 실제 쿼리를 실제 확인해 보면 limit, offset 쿼리가 적용 안 되는 것을 볼 수 있다.
select
distinct order0_.order_id as order_id1_6_0_,
member1_.member_id as member_i1_4_1_,
delivery2_.delivery_id as delivery1_2_2_,
orderitems3_.order_item_id as order_it1_5_3_,
order0_.delivery_id as delivery4_6_0_,
order0_.member_id as member_i5_6_0_,
order0_.order_date as order_da2_6_0_,
order0_.status as status3_6_0_,
member1_.city as city2_4_1_,
member1_.street as street3_4_1_,
member1_.zipcode as zipcode4_4_1_,
member1_.name as name5_4_1_,
delivery2_.city as city2_2_2_,
delivery2_.street as street3_2_2_,
delivery2_.zipcode as zipcode4_2_2_,
delivery2_.status as status5_2_2_,
orderitems3_.count as count2_5_3_,
orderitems3_.item_id as item_id4_5_3_,
orderitems3_.order_id as order_id5_5_3_,
orderitems3_.order_price as order_pr3_5_3_,
orderitems3_.order_id as order_id5_5_0__,
orderitems3_.order_item_id as order_it1_5_0__
from
orders order0_
inner join
member member1_
on order0_.member_id=member1_.member_id
inner join
delivery delivery2_
on order0_.delivery_id=delivery2_.delivery_id
inner join
order_item orderitems3_
on order0_.order_id=orderitems3_.order_id
하이버네이트는 경고 로그를 남기면서 모든 데이터를 DB에서 읽어오고, 메모리에서 페이징을 해버리게 되는 이슈가 생김
컬렉션 패치조인 한계
- 컬렉션 페치 조인은 1개만 사용이 가능하다 -> 컬렉션 둘 이상에 패치조인 사용하면 안 됨, 데이터 부정합 현상이 나타나게 된다.
- 페이징 처리를 할 수 없다.
- 컬렉션 패치 조인시 DB 조인으로 인해 데이터가 뻥튀기된다(N 테이블의 개수에 맞게 됨)
- DB 데이터를 메모리에서 적재해 페이징 하는 성능 이슈가 발생하게 됨
컬렉션 패치조인 최적화
컬렉션 패치조인하면 페이징이 불가능하다는 이슈를 알게 되었다.
그러면 어떻게하면 페이징 + 컬렉션 엔티티 조회를 최적화를 할 수 있을까?? 같이 알아보도록 하자. ✌✌
1. 먼저 ToOne(OneToOne, ManyToOne) 관계를 모두 패치조인한다. ToOne관계는 row 수를 증가시키지 않으므로 페이징 쿼리에 영향을 주지 않는다.(여기서는 Member, Delivery 엔티티)
2. 컬렉션 toMany(OneToMany, ManyToMany)는 지연 로딩으로 조회한다.
3. 위의 2번 과정시 1+N 문제가 발생하게 된다. 따라서 지연 로딩 성능 최적화를 위해 hibernate.default_batch_fetch_size, @BatchSize를 적용한다.
- hibernate.default_batch_fetch_size : 글로벌 설정
- @BatchSize: 개별 엔티티 최적화
- 이 옵션들을 사용하면 컬렉션이나, Proxy 객체를 한꺼번에 설정한 size 만큼 IN 쿼리로 조회한다.
- default_batch_fetch_size는 100~1000 사이를 권장한다. DB에 따라 다르지만 In 절 파라미터를 1000으로 제한하기도 한다. 또한 1000 이상 잡으면 DB에서 데이터를 가져와 애플리케이션에 불러오므로 순간 DB 부하가 증가할 수 도 있다
정리
- 컬렉션 엔티티 쿼리 호출수가 '1 + N -> 1 + 1'로 최적화 된다.
- 조인보다 DB 데이터 전송량이 최적화된다.
- 패치 조인 방식과 비교해서 쿼리 호출수가 약간 증가하지만, DB 데이터 전송량이 감소한다
- 컬렉션 페치 조인은 페이징이 불가능하지만 이 방법은 페이징이 가능하다.
- ToOne 관계는 패치 조인해도 페이징에 영향을 주지 않는다. 따라서 ToOne 관계는 패치조인으로 LazyLoading, 1+N 문제를 해결하고, 나머지는 hibernate.default_batch_fetch_size 옵션으로 최적화하자
'BackEnd(Java) > Spring Data JPA' 카테고리의 다른 글
조회 API 성능 최적화 (0) | 2023.06.13 |
---|---|
@OnetoMany 관계 삭제 (0) | 2022.04.21 |
부모 - 자식 엔티티 관계 삭제 (0) | 2022.04.19 |
생성 시간, 수정 시간 자동으로 값 넣기 (0) | 2022.03.29 |
JPA - Mysql 컬럼명 대문자 생성이 안될 때 (0) | 2022.03.24 |