현재 진행 중인 프로젝트에서는 유튜버 정보와 유튜브 영상 정보가 DB에 저장되어 있다.
영상이나 유튜버 정보 업데이트가 꼭 실시간으로 이루어져야 할 필요는 없다.
때문에 일정 주기로 업데이트하고 싶어졌다.
따라서 하루에 한 번씩 DB에 업데이트 쿼리를 날리는 방안을 선택하였다.
유튜브 영상 데이터는 앞으로도 DB에 꾸준히 쌓이는 대량 데이터이다.
스프링 스케줄러는 간단하고 주기적인 작업을 처리하는 데에는 적합하지만, 복잡한 대용량 데이터 처리나 실패 시 재시도와 같은 요구 사항이 있는 경우 스프링 배치와 같은 솔루션을 고려해볼 필요가 있다.
스프링 배치는 내부적으로 실행 이력(Job Repository)을 관리한다.
BATCH_JOB_EXECUTION 조회
BATCH_STEP_EXECUTION 조회
현재 DB 상황 : 업데이트가 필요한 1000개의 데이터가 저장되어 있다.
@Test
public void testScheduler() throws Exception {
long schedulerStartTime = System.currentTimeMillis();
schedulerService.updateExerciseInfo();
long schedulerEndTime = System.currentTimeMillis();
long schedulerDuration = schedulerEndTime - schedulerStartTime;
log.info("Scheduler Duration: " + schedulerDuration + "ms");
}
1000개 영상 데이터 업데이트 수행 속도 (89688ms)
@Test
public void testBatch() throws Exception{ // 33m
long batchStartTime = System.currentTimeMillis();
exerciseUpdateJobLauncherTestUtils.launchJob(new JobParameters());
long batchEndTime = System.currentTimeMillis();
long batchDuration = batchEndTime - batchStartTime;
log.info("Batch Duration: " + batchDuration + "ms");
}
1000개 영상 데이터 업데이트 수행 속도 (21495ms)
한 번 더 실행 (71ms)
스케줄러 실행 시간이 89688ms(1분 30초)이고, 배치의 실행 시간이 21000ms(21초)이다. 그래서 스케줄러가 4.3배 더 길다.
@Bean
public Step exerciseUpdateStep(JobRepository jobRepository, PlatformTransactionManager platformTransactionManager) {
int chunkSize = 100;
return new StepBuilder("exerciseUpdateStep", jobRepository)
.chunk(chunkSize, platformTransactionManager)
.reader(exerciseUpdateItemReader)
.writer(exerciseUpdateItemWriter)
.taskExecutor(new SimpleAsyncTaskExecutor()) // 멀티 스레드로 병렬 처리
.transactionManager(platformTransactionManager)
.build();
}
일정량 chunk의 데이터 (사용자 정의)를 한 번에 읽어서 처리 후 DB에 저장하는 방식을 사용
chunkSize
가 100으로 설정되어 있고 데이터가 총 1000개라면, 총 10개의 청크로 데이터가 나누어질 수 있다.
SimpleAsyncTaskExecutor
에 의해 별도의 스레드에서 실행된다.SimpleAsyncTaskExecutor
는 요청된 작업을 처리하기 위해 매번 새로운 스레드를 생성한다.(모든 스레드가 동시에 실행되는지는 실행 환경에 따라 달라질 수 있다)