티스토리 뷰
들어가기전
최근 운영중인 사이트에 대한 동적 스케줄러를 이용한 스케줄링 관리에 대한 블로그를 작성한 적이 있습니다. 이 후 스케줄러의 중요성에 대해 공부하면서 현재 운영중인 사이트의 몇몇 스케줄러에 대해 실질적인 사이트의 핵심 시스템인 부분들이 존재하였습니다. 이러한 시스템의 스케줄러 처리에 있어서 여러 이슈들에 대한 대응책이 적용되어 있지 않다는 부분들을 확인했습니다. 그리고 실질적인 대량의 데이터 처리를 하는 시스템이 었기에 어떻게 하면 좀 더 세부적으로 관리하고 이슈에 대한 로그를 관리할 수 있을지 고민하던 차에 Spring Batch에 대한 기술을 접하게 되었습니다. 이 글은 Spring Batch에 대한 개념과 실질적으로 경험하고 적용했던 예시를 바탕으로 지식을 공유하고 Spring Batch를 사용하지 않았던 분들에게 도움이 되고자 남기는 포스팅입니다. Spring Batch의 디테일한 내용들은 직접 프로젝트를 통해 경험해보시기를 바랍니다.
Spring Batch 란
대량의 데이터를 효율적으로 처리하기 위해 스프링에서 제공하는 배치 처리 프레임워크입니다. 일괄 처리(즉, 배치 처리)는 정기적으로 많은 데이터를 수집하고, 처리하며, 저장하는 작업을 자동화하는 데 필요한 기능을 제공합니다. Spring Batch는 특히 비즈니스 요구에 따라 데이터 변환, ETL(Extract, Transform, Load), 파일 처리, DB 작업 등을 수행할 수 있는 기능을 포함하고 있어 데이터 통합 및 백엔드 작업 자동화에 많이 사용됩니다.
Spring Batch의 특징
- 트랜잭션 관리: 각 Step은 트랜잭션을 통해 실행되어 데이터의 무결성을 유지할 수 있습니다.
- 재시도 및 재시작: 실패한 Job을 재시도하거나 특정 시점에서부터 재실행할 수 있어 오류 발생 시 유연한 대처가 가능합니다.
- Chunk 기반 처리: 데이터를 일정 단위로 나누어 처리하므로 대용량 데이터 처리에 효율적입니다.
- 스케줄링 연동: Quartz, cron과 같은 스케줄러와 통합하여 주기적으로 배치 작업을 실행할 수 있습니다.
Spring Batch와 Quartz 같은 스케줄러와의 차이점은?
- Spring Batch: 주로 대용량 데이터 처리, ETL(추출, 변환, 적재) 작업, 트랜잭션 관리, 오류 처리, 작업 재시작 등을 필요로 하는 배치 처리에 최적화된 프레임워크입니다. 데이터의 읽기-처리-쓰기 과정을 파이프라인 형태로 관리하는 데 중점을 둡니다.
- Quartz/Spring Scheduler: 특정 시간에 작업을 주기적으로 실행하기 위한 스케줄링 프레임워크입니다. Quartz는 복잡한 스케줄링(예: 크론 표현식, 시간대 설정 등)에 강점을 가지며, Spring Scheduler는 간단한 스케줄링 작업에 적합합니다.
Spring Batch는 대용량 배치 처리에 대한 세부적으로 분리하여 관리할 수 있다는 것에 용이할 뿐이지 스스로 주기적으로 작동하는 기능이 없습니다. Quartz 또는 Scheduler를 통해 작동하게 됩니다. 그러므로 Spring Batch와 Scheduler는 비교 대상이 될 수 없습니다.
Spring Batch의 주요 구성 요소
- Job
- 역할: 배치 작업의 전체 흐름을 정의하는 상위 개념으로, 배치 작업을 시작하는 단위입니다.
- 구성: 여러 개의 Step으로 구성되며, 특정 조건에 따라 Step을 실행하거나, 재시작 정책을 설정할 수 있습니다.
- 재시작 가능 여부 설정: .preventRestart(false) 옵션을 통해 재시작 허용 여부를 정의합니다.
- Step
- 역할: Job 내에서 개별 작업 단위로 동작하며, 실제 데이터 처리(읽기, 가공, 쓰기)가 이루어지는 핵심 단위입니다.
- 구성: 각 Step은 ItemReader, ItemProcessor, ItemWriter를 조합해 Chunk 방식으로 데이터를 처리하거나, Tasklet으로 단일 작업을 수행할 수 있습니다.
- 상태 관리: StepExecution 객체에 상태가 저장되며, 실패하거나 성공한 상태를 기록해 재시작 시 참고합니다.
- ItemReader
- 역할: 데이터를 읽어오는 역할을 하며, 외부 파일, 데이터베이스, API 등 다양한 소스에서 데이터를 가져올 수 있습니다.
- 예시: FlatFileItemReader(파일 읽기), JdbcCursorItemReader(DB에서 읽기), JpaPagingItemReader(JPA로 읽기) 등을 통해 다양한 데이터 소스와 연결됩니다.
- ItemProcessor
- 역할: ItemReader로부터 읽은 데이터를 가공, 변환하거나 필터링하여 ItemWriter로 전달할 준비를 합니다.
- 예시: 데이터를 특정 형식으로 변환하거나, 필요 없는 데이터를 필터링하는 데 사용합니다.
- 특징: 필요한 경우 데이터를 검증하거나 비즈니스 로직을 추가해 데이터의 품질을 보장할 수 있습니다.
- ItemWriter
- 역할: 가공된 데이터를 최종적으로 저장하거나 출력하는 역할을 수행합니다.
- 예시: DB에 데이터 삽입(JdbcBatchItemWriter), 파일에 기록(FlatFileItemWriter), API 호출 등 다양한 형태로 데이터를 저장할 수 있습니다.
- 특징: 일괄 저장(batch write)을 통해 대량 데이터 쓰기 성능을 최적화합니다.
- JobRepository
- 역할: Job 및 Step 실행의 메타데이터를 관리하여 작업 이력, 실패 및 성공 여부 등을 추적합니다.
- 특징: Job의 중단 및 재시작 시 작업 상태를 관리하고, JobInstance와 JobExecution의 관계를 통해 작업의 재실행 가능성을 제어합니다.
- 데이터베이스 저장: JobRepository는 주로 데이터베이스에 상태 정보를 저장합니다.
- JobLauncher
- 역할: Job을 실행하는 인터페이스로, 특정 시점에 Job을 시작하는 데 사용합니다.
- 구현: JobLauncher를 통해 주기적인 스케줄링 시스템(예: Quartz)과 통합하거나, 특정 이벤트가 발생할 때 배치를 시작할 수 있습니다.
- Chunk-Oriented Processing
- 역할: 데이터를 일정한 크기의 Chunk로 나누어 처리하는 방식으로, 대량 데이터를 읽고 처리하고 쓰는 과정에서 성능과 효율성을 높입니다.
- Chunk 단위 설정: Step에서 .chunk(size) 메서드로 Chunk 크기를 설정할 수 있으며, 한 Chunk 내에서 트랜잭션이 관리됩니다.
- 트랜잭션 보장: Chunk 크기 단위로 트랜잭션이 커밋되므로, 실패 시 이전 Chunk로 돌아가 재시작할 수 있습니다.
- Retry와 Skip
- Retry: 특정 오류 발생 시 설정된 횟수만큼 재시도할 수 있도록 지원합니다.
- Skip: 오류가 발생해도 해당 데이터를 무시하고 다음 데이터로 넘어가도록 설정할 수 있습니다.
- Job 및 Step Listener
- 역할: Job과 Step의 시작과 종료 시 특정 로직을 수행하도록 콜백을 제공하며, 로깅이나 알림 처리, 상태 검증 등의 기능을 구현할 수 있습니다.
- 종류: JobExecutionListener, StepExecutionListener 등이 있습니다.
Spring Batch의 Step 종류 알아보기
Spring Batch에서 Job은 Step들의 그룹으로 이루어져 있습니다. Job은 순차적으로 Step을 수행하게 됩니다. Job에 실행되는 Step들에도 여러 종류가 존재합니다.
1. TaskletStep
- 설명: Tasklet을 사용하여 단일 작업을 수행하는 Step입니다. 특정 비즈니스 로직을 한 번 실행하는 데 적합하며, Tasklet 인터페이스를 구현하여 원하는 작업을 정의할 수 있습니다.
- 사용 예: 파일 삭제, 단일 API 호출, 특정 초기화 작업 등.
Step step = stepBuilderFactory.get("taskletStep")
.tasklet((contribution, chunkContext) -> { // 단일 task 안에서 수행이 이루어 집니다.
System.out.println("Tasklet is executed");
return RepeatStatus.FINISHED;
}).build();
2. ChunkOrientedStep
- 설명: 데이터를 Chunk 단위로 처리하는 Step입니다. ItemReader, ItemProcessor, ItemWriter로 구성되며, 지정된 크기만큼 데이터를 읽고 처리하고 쓰는 단계를 반복하여 작업을 수행합니다.
- 사용 예: 대량의 데이터를 반복적으로 읽어와 가공하고 저장해야 하는 경우, 예를 들어, 대규모 파일을 데이터베이스에 저장하는 경우.
Step step = stepBuilderFactory.get("chunkStep")
.<String, String>chunk(10)
.reader(reader) // 데이터를 읽는 단계 (DB 데이터 또는 외부 데이터에 해당)
.processor(processor) // 데이터를 가공하는 단계
.writer(writer) // 데이터를 DB에 저장하는 단계
.build();
3. PartitionStep
- 설명: 데이터를 여러 파티션으로 나누어 병렬로 처리하는 Step입니다. 마스터-슬레이브 패턴으로, Partitioner가 데이터를 파티션으로 분할하고 각 파티션을 개별 StepExecution으로 처리하여 병렬 처리를 수행합니다.
- 사용 예: 대규모 데이터를 여러 파티션으로 나누어 병렬로 처리할 필요가 있는 경우. 예를 들어, 지역별 데이터를 병렬로 처리하는 경우.
Step step = stepBuilderFactory.get("partitionStep")
.partitioner("slaveStep", partitioner)
.step(slaveStep)
.gridSize(4)
.build();
4. JobStep
- 설명: 다른 Job을 Step으로 실행할 수 있게 하는 Step입니다. Job 내에서 서브 Job을 실행할 때 사용되며, 복잡한 워크플로우를 만들 수 있습니다.
- 사용 예: 특정 로직을 별도의 Job으로 분리하고, 이를 재사용하거나 다른 Job 내에서 실행할 경우.
Step step = stepBuilderFactory.get("jobStep")
.job(childJob)
.build();
5. FlowStep
- 설명: Flow를 기반으로 실행되는 Step입니다. 여러 Step을 묶어 흐름(Flow)으로 정의하고, 해당 흐름을 하나의 Step처럼 실행할 수 있습니다.
- 사용 예: 조건부 흐름 제어가 필요하거나, 여러 Step을 그룹으로 묶어서 순차적으로 실행할 때 유용합니다.
Flow flow = new FlowBuilder<Flow>("flow")
.start(step1)
.next(step2)
.build();
Step step = stepBuilderFactory.get("flowStep")
.flow(flow)
.build();
6. ScalableStep - Spring Batch 5 이상
- 설명: PartitionStep과 유사하게 데이터를 병렬로 처리할 수 있도록 해주는 Step으로, 작업을 자동으로 확장해 병렬화와 분산 처리가 가능하게 합니다. 이는 Spring Batch 5 이상에서 사용 가능합니다.
- 사용 예: 큰 규모의 데이터 세트를 효율적으로 처리하기 위해 고성능, 병렬 분산 처리가 필요할 때.
Step step = stepBuilderFactory.get("scalableStep")
.taskExecutor(taskExecutor)
.chunk(100)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
Spring Batch 실제 적용한 실습 코드
운영 중인 사이트에 적용한 방식에 대한 예제 코드입니다. 위 Step 종류 중 ChunkOrientedStep 방식을 적용하여 시스템을 구성하였습니다.
그렇다면 왜 Chunk 방식을 선택하였을까요? 현재 사용중인 시스템은 실적 정보를 조회하여 처리하는 시스템입니다. 아래는 현재 시스템의 프로세스에 대한 이미지 입니다.
1. 실적 정보를 가져오기 위해 외부 API를 통해 데이터를 조회합니다. 데이터 결과는 리스트 형태로 가져오게 됩니다.
2. 조회한 리스트 데이터의 Status 컬럼 값을 기준으로 데이터를 분류 합니다.
3. 분류한 이후 해당 데이터를 상태 값과 현재 디비에 존재하는 데이터 여부에 따라 업데이트 또는 신규 추가 처리를 하게 됩니다.
위의 내용 같이 현재 시스템은 3가지 구분된 과정을 통해 스케줄링이 돌고 있는 시스템이었습니다. 그렇다 보니 기본적인TaskletStep,JobStep, FlowStep 방식은 비슷한 맥락으로 간단한 단일 처리 시스템이 아니기에 부적합하다고 느꼈고, PartitionStep 과 ScalableStep를 적용하기에는 단일 서버를 운영중에 있기에 부적합하다고 생각이 들었습니다. 그렇지만 ChunkOrientedStep은 단위 별로 구분이 되어 있고 구분 별로 명확한 처리 역할이 나눠져 있기에 분리된 역할에 대한 코드 가독성과 Chunk를 통한 묶음 처리를 하기에 효율적인 일괄 처리가 될거라고 판단했습니다.
예제 코드
BatchConfig
- 실적 처리에 대한 배치 환경을 구축합니다.
@Configuration
@EnableBatchProcessing
public class RewardBatchConfig {
...
@Bean
public Job rewardJob(Step rewardStep,
RewardJobListener dateListListener) {
return new JobBuilder("rewardJob", jobRepository)
.incrementer(new RunIdIncrementer()) // Job의 고유 Id를 자동 증가 시켜줍니다.
.listener(dateListListener) // Batch 실행 전 처리에 사용되는 리스너입니다.
.start(rewardStep) // 배치를 실행하는 구간입니다.
.build();
}
@Bean
public Step rewardStep() {
return new StepBuilder("rewardStep", jobRepository)
.<JSONObject, RewardDTO>chunk(10, transactionManager) // 해당 Chunk처리는 10개 묶음 단위로 진행한다는 의미입니다.
.reader(rewardItemReader) // 데이터를 읽는 단계입니다.
.processor(rewardResponseProcessor) // 데이터를 필터 / 가공하는 단계입니다.
.writer(rewardItemWriter) // 데이터를 DB에 저장하는 단계입니다.
.faultTolerant() // 실패시 오류가 발생된 구간에서의 재시작을 합니다.
.retry(Exception.class) // 재시작의 기준을 설정합니다.
.retryLimit(3) // 재시작 최대 limit를 설정합니다.
.build();
}
}
JobExecutionListener
- 아래 리스너는 ItemReader 단계를 진행하기 전 실행되는 리스너입니다.
- beforeJob을 통해 Reader 단계가 실행되기전에 우선 처리되어야 할 내용들에 대해 작성하는 곳입니다.
- 아래 코드는 외부 API를 조회하는 조건은 날짜 별로 조회하는 것이기에 우선 미리 해당하는 날짜 리스트를 가져와 세팅을 해주고 있습니다.
- jobExecution에 조회된 날짜 리스트를 담아 다음 Step으로 전달하게 됩니다.
@Component
public class RewardJobListener implements JobExecutionListener {
...
@Override
public void beforeJob(@NotNull JobExecution jobExecution) {
// 외부 API에 조회할 날짜 데이터를 가져옴
dates = rewardRepository.findMaxRewardStatus100Check();
// 날짜 데이터가 없을 경우 오늘 날짜 기준으로 2달 전까지의 목록을 설정
if(dates.isEmpty()){
LocalDate today = LocalDate.now();
dates = new ArrayList<>();
for(int i=2; i>=1; i--) {
dates.add(today.minusMonths(i).toString().substring(0,7).replaceAll("-",""));
}
dates.add(today.toString().substring(0,7).replaceAll("-",""));
}
jobExecution.getExecutionContext().put("dateList", dates);
}
ItemReader
- ItemReader 단계에서 리스트로 조회한 정보를 토대로 하나씩 ItemProcessor 단계로 전달하게 됩니다. 반드시 단일 객체를 전달해야 합니다.
- 아래 read 메소드에서 dates 정보에 대한 내용은 이전 JobExecutionListener 으로 처리된 dataList를 가져오게 됩니다.
- 날짜 별로 getOrderList 메소드에 있는 외부 API 처리를 통해 실적 리스트 정보를 가져와 리스트 객체 하나씩 return 해줍니다.
- Reader단계에서 return 값이 null이 되면 더이상 전달할 값이 없다고 판단하게 되어 종료하게 됩니다.
@Component
public class RewardItemReader implements ItemReader<JSONObject> {
...
@Override
public JSONObject read() throws Exception {
if(dates == null){
// 현재 스텝의 context에서 dateList를 가져옴
StepContext stepContext = StepSynchronizationManager.getContext();
dates = (List<String>) stepContext.getStepExecution().getJobExecution().getExecutionContext().get("dateList");
}
logger.info(" read access ");
if(resultList == null || orderIndex >= resultList.size()) {
if (dateIndex < dates.size()) {
String date = dates.get(dateIndex++);
logger.info("### reward reader date={}",date);
resultList = getOrderList(date);
orderIndex = 0;
} else {
return null; // 리스트의 끝에 도달했으면 null 반환
}
}
return resultList.get(orderIndex++);
}
}
ItemProcessor
- Processor 단계에서는 이전 Reader단계에서 전달한 객체를 가지고 가공하는 단계입니다.
- 아래 process 메소드 내에서 JSONObject로 받아온 정보를 Status 값 별로 정리하여 값을 Return 하고 있습니다.
- 만약 process 메소드에서 return 값이 null인 경우에는 다음 Writer 단계에 전달되지 않습니다. 오로지 return이 되는 값들만이 다음 단계로 전달되게 됩니다.
@Component
public class RewardResponseProcessor implements ItemProcessor<JSONObject, RewardDTO> {
...
@Override
public RewardDTO process(JSONObject item) throws Exception {
logger.info(" proccess access ");
RewardSummaryInfoDTO reward = rewardRepository.findByTrlogId(tId);
if("100".equals(item.get("status"))) {
...
return reward
}else if("210".equals(item.get("status")) || "300".equals((String)item.get("status")) || "310".equals((String)item.get("status"))) {
...
return reward;
}
return null;
}
ItemWriter
- Processor단계를 거치게 된 후 최종 데이터베이스에 처리하는 구간에 해당합니다.
- ItemWriter는 write 메소드를 실행하게 됩니다. write가 받는 파라미터는 Chunk<? extends Object> 에 해당합니다.
- 해당 파라미터 내용은 Chunk를 리스트로 받아오게 되고 처음 BatchConfig에 설정된 개수 만큼을 받아 먼저 그룹 처리를 하게 됩니다.
- 이렇듯 Chunk는 하나하나 트랜잭션을 처리하는 것이 아닌 설정된 개수만큼을 묶어서 실행하여 처리하는 방식으로 일괄 처리를 통해 시간을 단축시킬 수 있습니다.
@Component
public class RewardItemWriter implements ItemWriter<RewardDTO> {
...
@Override
public void write(Chunk<? extends RewardDTO> chunk) throws Exception {
logger.info(" writer access ");
for(RewardDTO reward : chunk) {
...
if ("210".equals(reward.getRewardResultDTO().getStatus())) {
...
} else {
if ("0".equals(reward.getRstatus()) || "100".equals(reward.getRstatus()) || "300".equals(reward.getRstatus()) || "310".equals(reward.getRstatus())) {
...
}
}
}
}
BatchScheduler
- Spring Batch는 Job에 대한 세부적인 기능만 제공하지 실행 할 수는 없습니다. 결국 Quartz나 Scheduler의 힘을 빌려야합니다.
- 아래 코드 처럼 @Sheduled를 통해 실행을 하거나 Quartz를 이용하거나 또는 아래 주석과 같이 동적 스케줄러를 통해 실행할 수 있습니다.
- 아래 코드는 이전 동적 스케줄러를 적용했었기에 bean 정보와 메소드 정보를 디비에 저장하여 스케줄러를 실행하고 있습니다.
- JobParameters에 파라미터를 저장하게 되면 배치 테이블에 해당 정보들이 저장이 됩니다.
- jobLauncher.run을 통해 Spring Batch가 실행이 됩니다.
@Configuration
public class RewardResultBatchScheduler {
//@Scheduled(cron = "0 0 6 * * ?") // 매일 새벽 6시에 실행
//현재 코드는 @Scheduled를 사용하지 않고 동적 스케줄러를 이용하고 있습니다.
public void runRewardBatch() throws Exception {
System.out.println(" reward result batch start ");
int startTime = (int) System.currentTimeMillis();
JobParameters jobParameters = new JobParametersBuilder()
.addLong("time", System.currentTimeMillis())
.addLocalDateTime("datetime", LocalDateTime.now())
.toJobParameters();
jobLauncher.run(rewardJob, jobParameters); // rewardJob으로 설정된 batch가 실행됩니다.
int endTime = (int) System.currentTimeMillis();
System.out.println("spring batch reward : "+(endTime - startTime));
}
}
마지막으로..
예제로 제시한 내용은 저희 시스템에 맞다고 판단하여 Chunk 방식을 적용했습니다. Batch에는 위에 말했다시피 여러 Step 종류들이 존재합니다. 그렇기에 자신에게 맞는 방식을 찾고 어떤것이 더 적합한지 확인해보면서 적용해보는 것이 좋을 것이라고 봅니다. 저 또한 현 시스템에서는 Chunk 방식이 맞다고 판단하여 적용했지만 또 모르죠.. 이보다 다른 방식이 더 적합할 수 있을지도.. 저도 아직은 Spring Batch에 대해 아는 것이 부족하기에 지속적으로 공부해 가려고 합니다. 이 글을 보고 계실 여러분 또한 실습 예제는 참고로 보시고 나에게 더 맞는 방식이 뭔지 찾아보고 적용해보는 것을 추천드립니다.
추가 내용
Spring Batch는 Batch 관련 테이블을 제공합니다. JobRepository가 이에 해당합니다. 하지만 Spring Batch는 자동적으로 테이블을 생성해주지 않습니다. 해당하는 테이블들을 직접 선언해줘야합니다.. (이런건 좀 자동생성해주지..) 하지만 고맙게도 Spring Batch에서는 sql에 대한 정보들을 가지고 있습니다.
인텔리제이나 SpringBoot 개발 툴에서 schema-*을 검색해보면 여러 SQL별 코드를 확인할 수 있습니다. 아래 내용은 Mysql에 대한 Batch sql 정보입니다.
schema-mysql.sql
-- Autogenerated: do not edit this file
CREATE TABLE BATCH_JOB_INSTANCE (
JOB_INSTANCE_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_NAME VARCHAR(100) NOT NULL,
JOB_KEY VARCHAR(32) NOT NULL,
constraint JOB_INST_UN unique (JOB_NAME, JOB_KEY)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT ,
JOB_INSTANCE_ID BIGINT NOT NULL,
CREATE_TIME DATETIME(6) NOT NULL,
START_TIME DATETIME(6) DEFAULT NULL ,
END_TIME DATETIME(6) DEFAULT NULL ,
STATUS VARCHAR(10) ,
EXIT_CODE VARCHAR(2500) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED DATETIME(6),
constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
JOB_EXECUTION_ID BIGINT NOT NULL ,
PARAMETER_NAME VARCHAR(100) NOT NULL ,
PARAMETER_TYPE VARCHAR(100) NOT NULL ,
PARAMETER_VALUE VARCHAR(2500) ,
IDENTIFYING CHAR(1) NOT NULL ,
constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY ,
VERSION BIGINT NOT NULL,
STEP_NAME VARCHAR(100) NOT NULL,
JOB_EXECUTION_ID BIGINT NOT NULL,
CREATE_TIME DATETIME(6) NOT NULL,
START_TIME DATETIME(6) DEFAULT NULL ,
END_TIME DATETIME(6) DEFAULT NULL ,
STATUS VARCHAR(10) ,
COMMIT_COUNT BIGINT ,
READ_COUNT BIGINT ,
FILTER_COUNT BIGINT ,
WRITE_COUNT BIGINT ,
READ_SKIP_COUNT BIGINT ,
WRITE_SKIP_COUNT BIGINT ,
PROCESS_SKIP_COUNT BIGINT ,
ROLLBACK_COUNT BIGINT ,
EXIT_CODE VARCHAR(2500) ,
EXIT_MESSAGE VARCHAR(2500) ,
LAST_UPDATED DATETIME(6),
constraint JOB_EXEC_STEP_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT (
STEP_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT TEXT ,
constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT (
JOB_EXECUTION_ID BIGINT NOT NULL PRIMARY KEY,
SHORT_CONTEXT VARCHAR(2500) NOT NULL,
SERIALIZED_CONTEXT TEXT ,
constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;
CREATE TABLE BATCH_STEP_EXECUTION_SEQ (
ID BIGINT NOT NULL,
UNIQUE_KEY CHAR(1) NOT NULL,
constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;
INSERT INTO BATCH_STEP_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_STEP_EXECUTION_SEQ);
CREATE TABLE BATCH_JOB_EXECUTION_SEQ (
ID BIGINT NOT NULL,
UNIQUE_KEY CHAR(1) NOT NULL,
constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;
INSERT INTO BATCH_JOB_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_EXECUTION_SEQ);
CREATE TABLE BATCH_JOB_SEQ (
ID BIGINT NOT NULL,
UNIQUE_KEY CHAR(1) NOT NULL,
constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;
INSERT INTO BATCH_JOB_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_SEQ);
연관된 포스팅 글
https://ghgo195.tistory.com/77
참고자료
https://spring.io/projects/spring-batch/
'프로그램 언어 > Spring' 카테고리의 다른 글
Spring 스케줄러 (ThreadPoolTaskScheduler)를 이용한 동적 스케줄 관리 적용기 (2) - 로그 관리 (이벤트 리스너) (1) | 2024.10.20 |
---|---|
Spring 스케줄러 (ThreadPoolTaskScheduler)를 이용한 동적 스케줄 관리 적용기 (1) (4) | 2024.10.16 |
Spring Cloud OpenFeign 정리 (1) | 2024.10.16 |
Hazelcast Multicast / Tcp-ip 적용하기 (0) | 2024.02.29 |
hazelcast-client 적용하기(실전편2) (0) | 2024.02.27 |
- Total
- Today
- Yesterday
- MySQL
- docker
- 도커
- 권한
- centos7
- 알고리즘
- mybatis
- spring
- LocalDate
- dfs
- Quartz
- leatcode
- 캐시
- insert
- 개념 이해하기
- 격리수준
- 네이버 클라우드
- 캘린더
- 리눅스
- 정의
- ncp
- 이미지
- Java
- Cache
- Lock
- hazelcast
- dockerfile
- 스케줄러
- 컨테이너
- Linux
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |