[JPA] Delete의 성능이슈

BlackBean99·2022년 8월 21일
2

SpringBoot

목록 보기
11/20
post-custom-banner

Delete 성능이슈

1. deleteAllByIdIn : JPA 에서 제공하는 delete 메소드를 사용한 것이다.

@SpringBootTest
class CustomerRepositoryTest extends Specification {

    @Autowired
    private CustomerRepository customerRepository;

    def "Customer in 삭제" () {
        given:
        for(int i=0;i<100;i++){
            customerRepository.save(new Customer(i+"님"))
        }
        when:
        customerRepository.deleteByIdIn(Arrays.asList(1L,2L,3L))

        then:
        println "======= Then ======="
        customerRepository.findAll().size() == 97
    }
}

이 테스트로 확인을 해보면

  • in 쿼리로 조회하는 쿼리가 처음 실행된다.
  • id 별로 하나씩 delete된다.

JpaQueryException 클래스를 확인해서 delete로 찾아보면

jpaQuery.reateQuery(value) : select ~~ from Customer where ~~ 이 생성된다.

delete만 했는데 왜 select해??

연관관계가 없는 단건 DB 삭제시

단건 뿐 아니라 여러건을 삭제하더라도 먼저 조회를 하고 얻은 결과로 엔티티 데이터를 1건씩 삭제한다.

50만건 삭제를 한다고 하면 먼저 조회하고 단건씩 삭제한다.
RepositoryQuery-> AbstractJpaQuery 에 JpaQueryExcution 타입을 가진 DeleteExcution을 보면
JpaRepository에서 delete를 실행할때 jpaQuery에서 createQuery를 호출한다는 사실을 알 수 있습니다.

연관관계가 있는 DB 삭제시

cacade.DELETE 가 설정된 경우

  • in쿼리로 조회하는 쿼리가 처음 실행됩니다.
  • A Id별로 B을 조회한다.
  • 조회된 B을 1건씩 삭제한다.
  • 조회된 A을 1건씩 삭제한다.

이 과정은 연관관계가 없는 DB 삭제와 달리 B 조회는 왜 발생했는지는 em.remove 를 봐야합니다.

DefaultDeleteEventListener의 deleteEntity메소드 를 살펴보면

cacadeBeforeDelete메소드를 다시 파고들어야함을 알수 있습니다.

그리고 그 안에선 다시 Cascade.cascade 가 수행됩니다.

 persister.getPropertyValue( parent, i )
메소드가 수행되면

~~~ from A A0 where A.B_id=? 이런 쿼리가 실행됩니다.

부모를 지울 때, 자식도 같이 지워야 하니까 자식 Key를 기준으로 모두 가져와서 하나씩 삭제하는 것이다.

그럼 cacade 옵션을 끄면 작동하나요? 바로 삭제 쿼리로 넘어가긴 하지만

이런 문제가 에러 즉, FK를 맺고 있는 테이블이 있어서 삭제가 안됩니다!

그러면 성능 이슈를 없애기 위해 방법은?

해결책

1. 직접 범위 조건의 삭제 쿼리

@Transactional
@Modifying
@Query("delete from Customer c where c.id in :ids")
void deleteAllByIdInQuery(@Param("ids") List<Long> ids);

이런식으로 작성해서 쿼리를 짜면 효과적입니다.

연관관계가 있는 경우에는 위와 같은 에러가 발생하므로

이렇게 둘다 지워주면 됩니다.

Reference https://jojoldu.tistory.com/235?category=637935

profile
like_learning
post-custom-banner

0개의 댓글