일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- Java
- 잉여현금흐름
- 금리인하
- FCF
- 접근제어자
- 미국주식
- 백준
- mco
- 기업분석
- javascript
- 알고리즘
- 자바
- 주식
- object
- 무디스
- XLF
- 객체지향
- etf
- StringBuffer
- S&P500
- 배당성장
- 인플레이션
- 주린이
- 현금흐름표
- 제태크
- 프로그래머스
- 다형성
- 금리인상
- 그리디 알고리즘
- 오버라이딩
- Today
- Total
오늘의하루
JPA deleteByXX의 1+N 문제와 성능 저하 해결 방법 본문
JPA를 사용하면서 deleteByCouponId(Long couponId)와 같은 네이밍 메서드를 활용하면 간편하게 특정 ID를 기반으로 데이터를 삭제할 수 있지만 이러한 메서드를 사용할 때 몇 가지 주의해야 할 점이 있습니다.
deleteByCouponId의 동작 방식
JPA에서 deleteByCouponId와 같은 메서드를 실행하면 내부적으로 다음과 같은 과정을 거칩니다.
- 먼저 couponId를 기반으로 해당하는 엔티티 목록을 조회합니다.
- 조회된 엔티티들을 하나씩 순회하면서 EntityManager의 remove 메서드를 호출하여 삭제합니다.
- 삭제된 엔티티는 1개씩 flush 되며 트랜잭션이 걸려 있다면 커밋 시 최종 반영됩니다.
이 과정에서 단순히 deleteByCouponId를 호출하면 SQL의 DELETE FROM table WHERE coupon_id = ? 같은 단순한 쿼리가 실행될 것이라고 생각하기 쉽지만 실제로는 조회 후 반복문을 돌면서 삭제하는 방식으로 동작합니다.
deleteBy 메서드 사용 시 발생할 수 있는 문제
1. JPA 내부 코드
// JpaQueryExecution.class
protected Object doExecute(AbstractJpaQuery jpaQuery, JpaParametersParameterAccessor accessor) {
Query query = jpaQuery.createQuery(accessor); // 쿼리 제작 (조회용)
List<?> resultList = query.getResultList(); // 쿼리 실행
for(Object o : resultList) { // 반복
this.em.remove(o); // 실제 삭제 (이때 @Id 삭제)
}
return jpaQuery.getQueryMethod().isCollectionQuery() ? resultList : resultList.size();
}
2. 불필요한 조회 비용
만약 deleteByCouponId가 단순히 DELETE FROM 쿼리를 실행하는 것이 아니라면 먼저 해당하는 데이터를 조회하는 쿼리가 실행되기 때문에 데이터 양이 많을 경우 조회하는 비용이 커질 수 있습니다.
3. N+1 문제 발생 가능성
연관된 엔티티가 있는 경우 deleteByCouponId를 호출하면 연관된 엔티티들도 함께 조회될 가능성이 있습니다.
만약 Cascade 설정이 되어 있다면 삭제 시 연관 데이터를 추가적으로 조회하여 삭제하는 과정에서 N+1 문제가 발생할 수 있습니다.
4. 성능 저하 문제
조회된 데이터를 하나씩 삭제하는 방식이므로 데이터 개수가 많다면 불필요한 루프를 반복하는 문제가 발생합니다.
만약 1000개의 데이터를 삭제해야 한다면 단일 DELETE 쿼리 한 번으로 처리할 수 있는 작업을 1000번의 개별 DELETE 쿼리로 실행하게 되어 성능 저하를 유발할 수 있습니다.
해결 방법
@Modifying과 @Query를 활용한 직접 삭제
조회 없이 바로 삭제하는 방법으로는 @Modifying과 @Query를 사용하는 것이 있습니다.
@Repository
public interface CouponRepository extends JpaRepository<Coupon, Long> {
@Modifying
@Query("DELETE FROM Coupon c WHERE c.couponId = :couponId")
void deleteByCouponId(@Param("couponId") Long couponId);
}
이렇게 하면 조회 없이 즉시 삭제하는 DELETE FROM 쿼리가 실행되므로 성능이 향상됩니다.
- 만약 CouponId가 N개라면 IN 절을 활용해서 삭제할 수 있습니다.
[JPA] 벌크 연산(Bulk Operation) 알고 계신가요?
1. 벌크 연산 (Bulk Operation)이란?벌크 연산이란 N개의 데이터를 한 번에 UPDATE 또는 DELETE 하는 작업을 의미합니다.Spring Data JPA에서 벌크 연산을 수행하기 위해서는 @Modifying이 필요합니다.2. 왜 필요
jangto.tistory.com