본문 바로가기
프로젝트/FitTrip

[FitTrip] DELETE IN을 사용한 배치 처리로 얻을 수 있는 성능 향상

by 진꿈청 2024. 7. 2.

DELETE시 IN 사용

트러블 슈팅에서 DELETE IN 사용에 관한 내용을 다뤘다.

하지만, 해당 작업이 실제로 의미가 있는지가 중요하다.

 

따라서, 직접 서비스 구동시 DB에 데이터를 추가하여 성능이
얼마나 향상되었는지 확인하기 위한 작업을 진행했다.

 

작업 대상으로 삼은 API는 채널 삭제 API이다.

  • 포럼 채널에 속해있는 포럼 100개
  • 각 포럼과 연관되어 있는 파일 100개
  • 포럼 100개, 파일 10000개로 구성

 

 

서비스 구동시 DB Init 코드

@Slf4j
@Component
@RequiredArgsConstructor
public class BatchTest {

    private final UserRepository userRepository;
    private final ServerRepository serverRepository;
    private final ChannelRepository channelRepository;
    private final CategoryRepository categoryRepository;
    private final ForumRepository forumRepository;
    private final UserServiceFakeClient userServiceFakeClient;
    private final ServerUserRepository serverUserRepository;
    private final FileRepository fileRepository;

    @PostConstruct
    public void dbInit(){
        User user1 = User.of(userServiceFakeClient.getUser(1L));
        User user2 = User.of(userServiceFakeClient.getUser(2L));
        User user3 = User.of(userServiceFakeClient.getUser(3L));
        User user4 = User.of(userServiceFakeClient.getUser(4L));

        userRepository.save(user1);
        userRepository.save(user2);
        userRepository.save(user3);
        userRepository.save(user4);

        log.info("User dbInit 완료");

        Server server1 = Server.of("test1", null, 1L);
        Server server2 = Server.of("test1", null, 2L);

        serverRepository.save(server1);
        serverRepository.save(server2);

        log.info("Server dbInit 완료");

        ServerUser serverUser1 = ServerUser.of(server1, user1);
        ServerUser serverUser2 = ServerUser.of(server2, user1);
        ServerUser serverUser3 = ServerUser.of(server1, user3);

        serverUserRepository.save(serverUser1);
        serverUserRepository.save(serverUser2);
        serverUserRepository.save(serverUser3);

        log.info("ServerUser dbInit 완료");

        Category category1 = Category.of(server1, "채팅");
        Category category2 = Category.of(server1, "음성");
        Category category3 = Category.of(server2, "채팅");
        Category category4 = Category.of(server2, "음성");

        categoryRepository.save(category1);
        categoryRepository.save(category2);
        categoryRepository.save(category3);
        categoryRepository.save(category4);

        log.info("Category dbInit 완료");

        Channel channel1 = Channel.of(server1, 1L, ChannelType.CHAT, "채팅");
        Channel channel2 = Channel.of(server1, 2L, ChannelType.VOICE, "음성");
        Channel channel3 = Channel.of(server2, 3L, ChannelType.CHAT, "채팅");
        Channel channel4 = Channel.of(server2, 4L, ChannelType.VOICE, "음성");
        Channel forumChannel = Channel.of(server1, null, ChannelType.FORUM, "포럼");

        channelRepository.save(channel1);
        channelRepository.save(channel2);
        channelRepository.save(channel3);
        channelRepository.save(channel4);
        channelRepository.save(forumChannel);

        log.info("Channel dbInit 완료");


        for(int i = 0; i < 100; i++){
            Forum newForum = Forum.of(5L, "test" + i, user1, "test" +i, ForumCategory.CALORIE);
            forumRepository.save(newForum);

            for(int j = 0; j < 100; j++){
                File file = File.of(newForum, "test" + j);
                fileRepository.save(file);
            }
        }

        log.info("Forum & File dbInit 완료");
    }

}

 

위의 코드로 테스트하기 위한 임시 데이터를 삽입했다.

IN을 사용하지 않은 그냥 deleteAll() 메소드를 사용한 경우

image

    @OneToMany(mappedBy = "forum", orphanRemoval = true)
//    @OneToMany(mappedBy = "forum")
    private List<File> files = new ArrayList<>();

 

API 실행 결과

image

 

포럼 100개, 파일 10000개로 구성되어있는 경우 1.658sLatency10105개의 쿼리가 나갔다.

이는 어마어마한 수치이다. 만약, 상용 서비스였다면 훨씬 더 많은 데이터가 있을 것이며 동시에 큰 문제로 이어질 수 있다.

 

심지어 위에서 작업한 코드는 테스트를 위해 S3에 이미지가 올라가지 않은 File 엔티티를 사용한 코드다.
만약, S3 삭제 작업까지 더해진다면 상상도 하고 싶지 않다.

 

 

그렇다면 DELETEIN을 사용한다면 어떤 결과가 나올까?

 

DELETEIN을 사용한 경우

image

//    @OneToMany(mappedBy = "forum", orphanRemoval = true)
    @OneToMany(mappedBy = "forum")
    private List<File> files = new ArrayList<>();

 

API 실행 결과

image

 

Latency1/4로 줄고 쿼리는 단 7개만 나간다.
S3 삭제 작업은 그대로 유지되겠지만, 나가는 쿼리의 개수가 말도 안되게 줄었다.

 

 

데이터가 많아질수록 그 차이는 제가 테스트 해본 방법보다 커질 것으로 예상된다.

앞으로는 이번에 알게 된 내용을 꼭 적용하여 성능 최적화를 위한 노력을 해야겠다.