N번의 delete query와 batch delete

qpwoeiru·2024년 12월 21일
0
post-thumbnail

기존에 delete query를 jpa에서 제공하는 메소드 그대로 사용했었다.

groupMemberRepository.deleteByStudyGroup(group);

그런데 발생하는 delete 쿼리가 한 개가 아니었다.

실제로 4개의 GroupMember 엔티티를 위 메소드로 삭제하면 아래처럼 쿼리가 발생한다.

Hibernate: 
    delete 
    from
        group_member 
    where
        id=?
Hibernate: 
    delete 
    from
        group_member 
    where
        id=?
Hibernate: 
    delete 
    from
        group_member 
    where
        id=?
Hibernate: 
    delete 
    from
        group_member 
    where
        id=?

deleteBy- 로 시작하는 메소드를 사용해 데이터를 삭제하면 건수에 상관 없이 먼저 select로 조회한 후에 이에 대한 결과를 한 건씩 삭제한다.

N개의 레코드를 지운다면 N번의 delete 쿼리가 발생한다는 것이다. 지금은 엔티티 수가 많이 없으니 문제가 안되겠지만, 쿼리 수가 많은 건 디스크 I/O, 네트워크 오버헤드 등 성능 저하의 원인이 될 수 있다. 그래서 이를 해결할 수 있는 대안들을 실험해보았다.


1. deleteAll()

groupMemberRepository.deleteAll(groupMemberRepository.findAllByStudyGroup(studyGroup));

GroupMemberRepository에 해당 유저들이 존재하는지 먼저 select로 확인한 후, 이를 List<>로 만들어 deleteAll 에 넣어보았다.

Hibernate: 
    select
        gm1_0.id,
        gm1_0.is_visible,
        gm1_0.join_date,
        gm1_0.role,
        gm1_0.study_group_id,
        gm1_0.user_id 
    from
        group_member gm1_0 
    where
        gm1_0.study_group_id=?
Hibernate: 
    delete 
    from
        group_member 
    where
        id=?
Hibernate: 
    delete 
    from
        group_member 
    where
        id=?
Hibernate: 
    delete 
    from
        group_member 
    where
        id=?
Hibernate: 
    delete 
    from
        group_member 
    where
        id=?

하지만 여전히 delete query는 4번 나갔다. 게다가 파라미터로 입력 받은 GroupMember 리스트를 조회하는 메소드 때문에 select 쿼리도 한 번 수행하게 된다. deleteBy- 보다 더 비효율 적인 것이다.

deleteAll() 메소드 또한 리스트 하나하나에 대해 해당 id가 존재하는지 검사하며 delete를 진행한다. id 개수만큼 for문을 돌리게 되는 것이다. 이는 deleteBy- 만큼이나 성능 저하의 원인이 되므로 사용하지 않는다.


2. deleteAllInBatch()

이번에는 Spring Data JPA에 있는 deleteAllInBatch() 메소드를 사용해봤다. 한 번의 쿼리로 데이터들을 삭제할 수 있다.

groupMemberRepository.deleteAllInBatch(groupMemberRepository.findAllByStudyGroup(studyGroup));
Hibernate: 
    select
        gm1_0.id,
        gm1_0.is_visible,
        gm1_0.join_date,
        gm1_0.role,
        gm1_0.study_group_id,
        gm1_0.user_id 
    from
        group_member gm1_0 
    where
        gm1_0.study_group_id=?
Hibernate: 
    delete gm1_0 
    from
        group_member gm1_0 
    where
        gm1_0.id=? 
        or gm1_0.id=? 
        or gm1_0.id=? 
        or gm1_0.id=?

batch query라 확실하게 많이 줄었다. 처음 select문으로 delete 할 GroupMember 레코드들을 조회하고, 해당 쿼리로 받아온 엔티티들을 batch delete로 제거한다. deleteAll 보단 deleteAllInBatch 를 사용하는 게 확실히 나은 것 같다.


3. delete 쿼리 직접 작성

이번엔 where 절을 직접 작성해보기로 한다.

@Modifying
@Query("delete from GroupMember gm where gm.studyGroup = :studyGroup")
void deleteAllByStudyGroup(StudyGroup studyGroup);
Hibernate: 
    delete gm1_0 
    from
        group_member gm1_0 
    where
        gm1_0.study_group_id=?

@Query 작성한 그대로 수행한다. 사실 가장 좋은 방법이다. 웬만하면 조건부로 데이터를 삭제해야할 땐 직접 쿼리를 작성하는 방법으로 진행하자.


참고
https://monsters-dev.tistory.com/88
https://jojoldu.tistory.com/235
https://frogand.tistory.com/172

0개의 댓글

관련 채용 정보