Spring Batch 병렬 처리
Spring Batch는 대용량 데이터를 처리하는 프레임워크 입니다.
병렬 처리는 한 번에 여러 작업을 동시에 실행하여 작업을 분산시키는 방법이며 성능 향상을 위한 중요한 기능 중 하나 입니다.
이번에는 TaskExecutor를 사용하여 step 내부를 Chunk 단위로 병렬 처리한 경험을 기록하려 합니다.
스레드 풀 설정
Spring Batch 병렬 처리를 위해 스레드 풀 설정을 진행합니다.
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(4); // 코어 스레드 개수 설정
taskExecutor.setMaxPoolSize(8); // 최대 스레드 개수 설정
taskExecutor.setQueueCapacity(10); // 큐 크기 설정
return taskExecutor;
}
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
ThreadPoolTaskExecutor 클래스는 Spring의 TaskExecutor 인터페이스를 구현한 것으로 다중 스레드를 관리하고 작업을 실행하는데 사용됩니다. 이 코드를 통해 병렬 처리를 활성화하고 다수의 스레드를 사용하여 작업을 분산 처리할 수 있게 합니다.
taskExecutor.setCorePoolSize(4);
이 메소드를 통해 코어 스레드 개수를 설정합니다.
코어 스레드 개수는 동시에 실행되는 스레드의 최소 개수를 나타냅니다.
일반적으로 CPU의 코어 수 만큼 코어 스레드를 설정합니다.
taskExecutor.setMaxPoolSize(8);
이 메소드를 통해 허용되는 최대 스레드 개수를 설정합니다.
최대 스레드 개수는 동시에 실행되는 스레드의 최대 개수를 나타냅니다.
작업 부하가 증가하면서 새로운 작업이 큐에 추가될 때 최대 스레드 개수까지 스레드가 생성될 수 있습니다.
최대 스레드 개수를 초과하는 작업은 큐에 대기하게 됩니다.
taskExecutor.setQueueCapacity(10);
이 메소드를 통해 작업 큐의 최대 크기를 설정합니다.
작업 큐 크기는 대기 중인 작업을 보관하는 큐의 최대 용량을 나타냅니다.
스레드 풀의 코어 스레드 수와 큐 크기를 조절하여 일정 부하를 유지하도록 설정합니다.
이러한 설정을 통해 적절한 코어 스레드 개수와 큐 크기를 조절하여 작업의 처리량과 작업 큐의 부하를 효과적으로 관리할 수 있습니다.
설정한 TaskExecutor 적용
@Bean
public Step userTierStepV2() {
return stepBuilderFactory.get("userStepV2")
.<User, User>chunk(100)
.reader(jpaPagingItemReaderV2())
.processor(userProcessorV2())
.writer(jpaItemWriterV2())
.taskExecutor(taskExecutor()) // taskExecutor 적용
.build();
}
.taskExecutor(taskExecutor())
이 메소드를 통해 설정한 TaskExecutor를 적용하여 병렬 처리, 분산 처리를 가능하게 합니다.
성능 비교
기존 방식
2023-08-23 22:40:13.189 INFO 13572 --- [nio-8080-exec-6] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=userJobV1]] launched with the following parameters: [{time=1692798013181}]
2023-08-23 22:40:13.196 INFO 13572 --- [nio-8080-exec-6] o.s.batch.core.job.SimpleStepHandler : Executing step: [userStepV1]
2023-08-23 22:40:19.935 INFO 13572 --- [nio-8080-exec-6] o.s.batch.core.step.AbstractStep : Step: [userStepV1] executed in 6s739ms
2023-08-23 22:40:20.008 INFO 13572 --- [nio-8080-exec-6] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=userJobV1]] completed with the following parameters: [{time=1692798013181}] and the following status: [COMPLETED] in 6s794ms
2023-08-23 22:40:20.008 INFO 13572 --- [nio-8080-exec-6] com.dotd.user.aop.LoggingAspect : 위치 : BatchController.setTier() / 걸린 시간 : 6827 ms
병렬 처리
2023-08-23 22:42:05.646 INFO 13572 --- [nio-8080-exec-9] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=userJobV2]] launched with the following parameters: [{time=1692798125638}]
2023-08-23 22:42:05.652 INFO 13572 --- [nio-8080-exec-9] o.s.batch.core.job.SimpleStepHandler : Executing step: [userStepV2]
2023-08-23 22:42:07.024 INFO 13572 --- [nio-8080-exec-9] o.s.batch.core.step.AbstractStep : Step: [userStepV2] executed in 1s372ms
2023-08-23 22:42:07.028 INFO 13572 --- [nio-8080-exec-9] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=userJobV2]] completed with the following parameters: [{time=1692798125638}] and the following status: [COMPLETED] in 1s380ms
2023-08-23 22:42:07.028 INFO 13572 --- [nio-8080-exec-9] com.dotd.user.aop.LoggingAspect : 위치 : BatchController.setTierVersionTwo() / 걸린 시간 : 1390 ms
chunk 사이즈 100개로 데이터 10000건을 대상으로 사용자의 사용 금액에 따라 등급을 정하는 로직을 돌렸을 때 나온 결과 입니다.
기존 처리 방식은 6827ms 인 반면 병렬 처리한 방식은 1390ms로 눈에 띄게 빨라진걸 확인할 수 있습니다.
병렬 처리 방식은 데이터 건수가 많을수록 그 성능차이가 더 크게 나타납니다.
마무리
이번에는 TaskExecutor로 멀티 스레드로 Batch를 처리하도록 했습니다.
병렬 처리를 적용하여 기존 방식과 비교한 결과 눈에 띄게 빨라진 것을 확인했으며 대용량 데이터를 처리할 때 성능 향상에 중요한 역할을 한다는 것을 배웠습니다.
하지만 이것으로 끝이 아닙니다. Spring Batch는 flow, split을 지원하여 작업의 흐름을 설정할 수 있으며 어떤 작업들을 병렬 처리할 것인지 배치할 수 있습니다.
다음 글에는 flow, split를 활용해 작업 흐름을 설정해보도록 하겠습니다.
참고 사이트
https://devfunny.tistory.com/833
[SpringBatch 실습] 31. 스프링 배치 Parallel Steps(병렬) 수행하여 분석
Parallel Steps SplitState를 사용하여 여러개의 Flow들을 병렬적으로 실행하는 구조이다. 실행이 다 완료된 후 FlowExecutionStatus 결과들을 취합해서 다음 단계를 결정한다. Job 생성 ParallelStepConfiguration.java
devfunny.tistory.com