개발/Java

레거시 스케줄러에 배치 처리 방식 적용하기

반응형

기존 프로젝트에서 레거시 스케줄러 코드에 배치 형식을 적용한 코드를 공유한다.

 

레거시 스케줄러에서도 배치 형식을 적용되어 있었다(ex. 백만건 -> 1000건 단위 배치 작업)

1000건을 threadPoolExecutor(thread-size : 10)를 사용하여 작업을 처리 하였는데

 

[AS-IS] 

for(File file : fileList){

    threadPoolExecutor.submit(new BulkFile(file,....));

}

 

이렇게 submit이 1000번 호출되고 10개는 스레드 풀이 작업을 나머지 990개는 queue에 쌓였다(스레드가 하나씩 가져와서 작업).

스레드가 하나씩 가져와서 작업하는 방식은 context-switching이 자주 발생하고 불필요한 오버해드가 많이 발생해서 비효율적인 방식이다.

 

그래서 다음과 같이 개선하였다:

  • 전체 파일 리스트를 threadSize 기준으로 나누어 각 스레드가 일정량의 작업을 연속적으로 처리하도록 변경
  • Guava의 Lists.partition()을 활용해 배치 분할
  • 각 배치는 하나의 쓰레드에서 순차적으로 처리

[TO-BE]

List<List<File>> batchs = Lists.partition(fileList, listSize/threadSize);

for(List<File> batch : batchs){

     threadPoolExecutor.submit(()->{

             for(File file : batch){

                    new BulkFile(file,...).run();

              }

     });

}

 

이렇게하면 1000개 작업을 100개씩 10개 배치로 나눠서 하나의 스레드가 100개씩 작업하게 된다.

 

이 방식의 장점은 다음과 같다:

  • 각 스레드가 쉬지 않고 일정량의 작업을 처리하게 되어 스레드 리소스를 효율적으로 사용
  • 작업 큐에 데이터가 쌓이지 않고 즉시 실행
  • 전체적으로 오버헤드가 줄어들고 성능이 개선

성능

  • 동일한 1,000건 작업 기준으로, 기존에는 약 1분 정도 걸리던 작업이 개선 후에는 30초 이하로 단축됨을 확인

주의할 점

  • 하나의 배치에서 예외가 발생하면 해당 스레드의 모든 작업이 중단될 수 있으므로, 적절한 예외 처리 필수
  • submit()에서 람다를 사용할 경우 run()을 명시적으로 호출해야 동작(new BulkFile(...).run();)

 

레거시 코드라고 해서 무조건 그대로 사용하는 것이 아니라, "왜 이렇게 되어 있을까?", "더 나은 방식은 없을까?" 를 끊임없이 고민하며 개선해나가는 것이 중요하다는 걸 다시금 느꼈던 경험이었다.

 

반응형

'개발 > Java' 카테고리의 다른 글

Spring ResponseEntity Stream 처리 관련 오류  (2) 2025.07.25
대용량 파일 관리 스케줄러 개발 정리  (0) 2025.03.04
자바 기초1  (0) 2021.03.30