새소식

framework/spring-batch

[Spring Batch] 스프링 배치 기초, 메타 데이터와 도메인

  • -

스프링 배치(Spring Batch)

스프링 배치란?

Spring Batch는 대용량 일괄처리의 편의를 위해 설계된 가볍고 포괄적인 배치 프레임워크이다.
로깅/추적, 트랜잭션 관리, 작업 처리 통계, 작업 재시작, 건너뛰기, 리소스 관리 등 대용량 레코드 처리에 필수적인 재사용 가능한 기능을 제공한다.
또한 최적화 및 파티셔닝 기술을 통해 대용량 및 고성능 일괄 작업을 가능하게 하는 고급 기술 서비스 및 기능을 제공한다.

Spring Batch Meta Data

스프링 배치는 배치의 실행 및 관리를 위한 목적으로 여러 도메인들(Job, Step, JobParameters 등)의 정보들을 저장, 업데이트, 조회할 수 있는 스키마를 제공한다.
과거, 현재의 실행에 대한 자세한 정보, 실행에 대한 성공과 실패 여부 등을 일목요연하게 관리함으로서 배치 운용에 있어 리스크 발생시 빠른 대처가 가능하다.

스프링 배치 라이브러리에서 DB 스키마를 제공한다.
org.springframework.batch:spring-batch-core 라이브러리의 org.springframework.batch.core 위치에 DB별 스키마 파일이 존재한다.
DB에 맞는 스키마를 복사해서 수동으로 테이블을 생성할 수도 있고 아래와 같이 설정을 통해 자동 생성할 수도 있다.

spring:
    batch:
      jdbc:
        initialize-schema: always # [always || embedded (default) || never]
  • spring.batch.jdbc.initialize-schema 옵션을 설정한다.
    • always : 항상 스크립트를 실행한다.
    • embedded : 내장 DB일 때만 실행되어 스키마가 자동 생성된다.
    • never : 스크립트를 실행하지 않는다.
    • 메타 테이블이 생성되어있지 않다면 오류가 발생하여 애플리케이션 실행에 실패한다.

스키마에는 6개의 메타 테이블과 3개의 시퀀스 테이블이 존재한다.
메타 테이블들을 살펴보겠다.

Job 관련 테이블

BATCH_JOB_INSTANCE

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)
)

BATCH_JOB_INSTANCE 테이블에는 Job이 실행될 때 해당 Job 인스턴스의 정보가 저장된다.

  • JOB_INSTANCE_ID : 고유하게 식별할 수 있는 기본키
  • VERSION : 업데이트 될 때마다 1씩 증가
  • JOB_NAME : Job을 구성할 때 부여하는 Job 이름
  • JOB_KEY : job_namejob_Parameter를 합쳐 해싱한 값을 지정

BATCH_JOB_EXECUTION

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),
    JOB_CONFIGURATION_LOCATION VARCHAR(2500) NULL,
    constraint JOB_INST_EXEC_FK foreign key (JOB_INSTANCE_ID)
    references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)
)

BATCH_JOB_EXECUTION 테이블에는 Job의 실행 정보가 저장된다.
Job 생성, 시작, 종료 시간 및 실행 상태, 메시지 등을 관리한다.

  • JOB_EXECUTION_ID : JobExecution을 고유하게 식별할 수 있는 기본키로 JOB_INSTANCE와 일대다 관계이다.
  • VERSION : 업데이트 될 때 마다 1씩 증가
  • JOB_INSTANCE_ID : JOB_INSTANCE의 키
  • CREATE_TIME : Execution이 생성된 시점을 기록
  • START_TIME : Execution이 시작된 시점을 기록
  • END_TIME : Execution이 종료된 시점을 기록. Job 실행 도중 오류가 발생해서 Job이 중단된 경우, 값이 저장되지 않을 수 있다.
  • STATUS : 실행 상태를 저장. [ COMPLETED | FAILED | STOPPED | … ]
  • EXIT_CODE : 실행 종료 코드 저장 [ COMPLETED | FAILED | … ]
  • EXIT_MESSAGE : STATUS가 실패일 경우 실패 원인 등을 저장
  • LAST_UPDATED : 마지막 Execution 시점을 기록

BATCH_JOB_EXECUTION_PARAMS

CREATE TABLE BATCH_JOB_EXECUTION_PARAMS  (
    JOB_EXECUTION_ID BIGINT NOT NULL ,
    TYPE_CD VARCHAR(6) NOT NULL ,
    KEY_NAME VARCHAR(100) NOT NULL ,
    STRING_VAL VARCHAR(250) ,
    DATE_VAL DATETIME(6) DEFAULT NULL ,
    LONG_VAL BIGINT ,
    DOUBLE_VAL DOUBLE PRECISION ,
    IDENTIFYING CHAR(1) NOT NULL ,
    constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
    references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
)

BATCH_JOB_EXECUTION_PARAMS 테이블은 Job과 함께 실행되는 Job의 Parameter 정보들을 저장한다.

  • JOB_EXECUTION_ID : JobExecution 식별키 값. JOB_EXECUTION과 일대다 관계
  • TYPE_CD : STRING, LONG, DATE, DOUBLE 등 타입 정보
  • KEY_NAME : 파라미터 키 값
  • STRING_VAL : 파라미터 문자 값
  • DATE_VAL : 파라미터 날짜 값
  • LONG_VAL : 파라미터 LONG
  • DOUBLE_VAL : 파라미터 DOUBLE
  • IDENTIFYING : 식별여부 (true | false)

BATCH_JOB_EXECUTION_CONTEXT

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)
)

BATCH_JOB_EXECUTION_CONTEXT 테이블은 Job의 실행동안 여러가지 상태정보, 공유 데이터를 직렬화하여 저장해둔다.
Step 간에 서로 데이터를 공유할 수 있다.

  • JOB_EXECUTION_ID : JobExecution 식별키, JOB_EXECUTION 마다 각각 생성
  • SHORT_CONTEXT : Job의 실행 상태 정보, 공유 데이터 등의 정보를 문자열로 저장
  • SERIALIZED_CONTEXT : 직렬화된 전체 컨텍스트

Step 관련 테이블

BATCH_STEP_EXECUTION

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,
    START_TIME DATETIME(6) NOT 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)
)

BATCH_STEP_EXECUTION 테이블은 Step의 실행 정보가 저장되며 Step 생성, 시작, 종료 시간 및 실행 상태, 메시지 등을 관리한다.

  • STEP_EXECUTION_ID : Step의 실행 정보를 고유하게 식별할 수 있는 기본키
  • VERSION : 업데이트 될 때마다 1씩 증가
  • STEP_NAME : Step을 구성할 때 부여하는 이름
  • JOB_EXECUTION_ID : JobExecution 기본키. JOB_EXECUTION과 일대다 관계
  • START_TIME : Execution이 시작된 시점을 기록
  • END_TIME : Execution이 종료된 시점을 기록
  • STATUS : 실행 상태를 저장
  • COMMIT_COUNT : 트랜잭션 당 커밋되는 수를 기록
  • READ_COUNT : 실행 시점에 ReadItem 수를 기록
  • FILTER_COUNT : 실행도중 필터링된 Item 수를 기록
  • WRITE_COUNT : 실행도중 저장되고 커밋된 Item 수를 기록
  • READ_SKIP_COUNT : 실행도중 ReadSkipItem 수를 기록
  • WRITE_SKIP_COUNT : 실행도중 WriteSkipItem 수를 기록
  • PROCESS_SKIP_COUNT : 실행도중 ProcessSkipItem 수를 기록
  • ROLLBACK_COUNT : 실행도중 Rollback이 일어난 수를 기록
  • EXIT_CODE : 실행 종료 코드를 저장
  • EXIT_MESSAGE : STATUS가 실패일 경우 실패 원인 등의 내용을 저장
  • LAST_UPDATED : 마지막 실행 시점을 기록

BATCH_STEP_EXECUTION_CONTEXT

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)
)

BATCH_STEP_EXECUTION_CONTEXT 테이블은 Step의 실행동안 여러가지 상태 정보, 공유 데이터를 직렬화하여 저장해둔다.
Step 별로 저장되며 서로 다른 Step 간에 서로 공유할 수 없다.

  • STEP_EXECUTION_ID : StepExecution 식별키 값, STEP_EXECUTION 마다 각각 생성
  • SHORT_CONTEXT : Step의 실행 상태 정보, 공유 데이터 등의 정보를 문자열로 저장
  • SERIALIZED_CONTEXT : 직렬화된 전체 컨텍스트

스프링 배치 도메인 이해

Job

Job은 배치 계층 구조에서 가장 상위에 있는 개념으로 하나의 배치 작업 자체를 의미한다.
Job Configuration을 통해 생성되는 객체 단위로서 배치 작업을 어떻게 구성하고 실행할 것인지 전체적으로 설정하고 명세해 놓은 객체이다.
여러 Step을 포함하고 있는 컨테이너로서 반드시 한개 이상의 Step으로 구성해야 한다.

스프링 배치는 Job 인터페이스에 대한 여러 기본 구현체를 제공한다.
대표적으로 SimpleJobFlowJob이 있다.

SimpleJob은 순차적으로 Step을 실행시키는 Job이다.
모든 Job에서 유용하게 사용할 수 있는 표준 기능을 갖고 있다.

FlowJob은 특정한 조건과 흐름에 따라 Step을 구성하여 실행시키는 Job이다.
Flow 객체를 실행시켜서 작업을 진행한다.

JobInstance

JobInstanceJob이 실행될 때 생성되는 Job의 논리적 실행 단위로서 고유하게 식별 가능한 작업의 실행을 나타낸다.
Job의 설정과 구성은 동일하지만, Job이 실행되는 시점에 처리하는 내용은 다르기 때문에 Job의 실행을 구분해야 한다.

JobInstanceJobJobParameters로 존재 여부를 판단한다.
Job의 이름과 JobParameters의 해시값인 JobKeyBATCH_JOB_INSTANCE 테이블을 조회하여 동일한 데이터가 없다면 새로운 JobInstance를 생성하고, 이미 데이터가 존재한다면 해당 데이터를 통해 기존 JobInstance를 만들어 반환한다.

JobInstance_image

JobParameter

JobParameterJob을 실행할 때 함께 포함되어 사용되는 파라미터를 가진 도메인 객체를 말하며 하나의 Job에 존재할 수 있는 여러개의 JobInstance를 구분하기 위한 용도로 사용된다.

JobParameterJobParameterBuilderDefaultJobParametersConverter를 통해 코드로 직접 생성하여 전달할 수도 있고, 애플리케이션 실행시에 주입시켜 줄 수도 있다.

JobParameterBuilder 사용

스프링 배치의 Job을 수동으로 실행하도록 설정하고 JobParamterBuilder를 통해 JobParameters를 생성하고 Job에 전달한다.

application.yml에 다음과 같이 작성하여 Job을 수동으로 실행할 수 있도록 한다.

spring:
  batch:
    job:
      enabled: false # 스프링 배치 자동 실행 off

스프링 배치 Job 구성 설정에서 등록한 Job을 의존 주입받아 JobLauncher를 통해 실행한다.
이때, JobParametersBuilder를 통해서 JobParameters를 생성하고 이를 JobLaucher에 함께 전달하여 사용한다.

@Component
public class JobRunner implements ApplicationRunner {

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job job;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // JobParameters 생성
        JobParameters jobParameters = new JobParametersBuilder()
                .addString("name", "user2")
                .toJobParameters();

        // JobLauncher를 통한 Job 수동 실행
        jobLauncher.run(job, jobParameters);
    }
}

동일한 Job에 동일한 파라미터로 실행한 정보가 저장되어 있으면 다음과 같은 예외를 반환하며 Job을 실행하지 않는다.

JobParameters_Exist

애플리케이션 실행 시점에 주입

다음과 같이 애플리케이션 실행 시점에 파라미터 정보를 주입해서 사용할 수도 있다.

$ java -jar spring-batch-0.0.1-SNAPSHOT.jar --spring.profiles.active=mysql name=user3

long, double 등의 타입 정보도 함께 전달해주려면 다음과 같이 작성할 수 있다.

$ java -jar spring-batch-0.0.1-SNAPSHOT.jar --spring.profiles.active=mysql name=user3 seq(long)=3L date(date)=2022/01/01 age(double)=26.9

참고 : zsh 오류

zsh에서 다음과 같이 입력하면 no matches found 오류가 발생한다.

$ java -jar spring-batch-0.0.1-SNAPSHOT.jar --spring.profiles.active=mysql name=user3 seq(long)=3L date(date)=2022/01/01 age(double)=26.9
> zsh: no matches found: seq(long)=3L

아래와 같이 작성하여 해결할 수 있다.

$ java -jar spring-batch-0.0.1-SNAPSHOT.jar --spring.profiles.active=mysql name=user3 'seq(long)=3L' 'date(date)=2022/01/01' 'age(double)=26.9' 

참고 : 인텔리제이 애플리케이션 실행 시점 주입 방법

다음과 같이 설정하여 파라미터 값을 전달해줄 수 있다.

인텔리제이 애플리케이션 실행 시점 주입

JobExecution

JobExecutionJobInstance에 대한 한 번의 시도를 의미하는 객체로서 Job 실행중에 발생한 정보들을 저장하고 있는 객체이다.
JobExecution은 시작 시간, 종료 시간, 상태(시작됨, 완료, 실패), 종료 상태의 속성을 가진다.

JobExecutionFAILED, COMPLETED 등의 Job 실행 결과 상태를 가지고 있다(BATCH_JOB_EXECUTION.STATUS 필드).
JobExecution의 실행 상태 결과가 FAILED이면 JobInstance 실행이 완료되지 않은 것으로 간주하여 재실행이 가능하다.
하지만 JobExecution 실행 상태 결과가 COMPLETED이면 실행이 완료된 것으로 간주하여 재실행이 불가능하다.
따라서 실행 상태 결과가 COMPLETED가 될 때 까지 하나의 JobInstance 내에서 여러번의 시도가 생길 수 있다.

아래는 JobExecution 객체의 생성 흐름을 나타낸 것이다.

JobExecution_생성흐름_이미지

Step

Step은 Batch Job을 구성하는 독립적인 하나의 단계로서 실제 패치 처리를 정의하고 컨트롤하는데 필요한 모든 정보를 가지고 있는 도메인 객체이다.
단순한 단일 태스크 뿐만 아니라 입력과 처리, 출력과 관련된 복잡한 비즈니스 로직을 포함하는 모든 설정들을 담고 있다.
Step은 배치 작업을 어떻게 구성하고 실행할 것인지를 명세한 Job의 세부 작업을 Task 기반으로 설정하고 명세해 놓은 객체이다.

스프링 배치는 Step 인터페이스에 대한 여러 기본 구현체를 제공한다.
TaskletStep, PartitionStep, JobStep, FlowStep 등이 있다.

TaskletStep은 가장 기본이 되는 클래스로서 Tasklet 타입의 구현체들을 제어한다.
PartitionStep은 멀티 스레드 방식으로 Step을 여러 개로 분리해서 실행한다.
JobStepStep 내에서 Job을 실행하도록 한다.
FlowStepStep 내에서 Flow를 실행하도록 한다.

StepExecution

StepExecutionStep에 대한 한 번의 시도를 의미하는 객체이다.
Step 실행중에 발생한 정보들을 저장하고 있다.
Step이 매번 시도될 때마다 생성되며 각 Step 별로 생성된다.
Job이 재시작 하더라도 이미 성공적으로 완료된 Step은 재실행되지 않고 실패한 Step만 실행된다.
만약 이전 단계의 Step이 실패해서 현재 Step을 실행하지 않았다면 현재 StepStepExecution은 생성되지 않는다.
Step이 실제로 시작되었을 때만 StepExecution을 생성한다.

StepContribution

StepContribution은 청크 프로세스의 변경 사항을 버퍼링 한 후, StepExecution 상태를 업데이트하는 도메인 객체이다.
청크 커밋 직전에 StepExecutionapply 메서드를 호출하여 상태를 업데이트 한다.

일반적으로 StepExecutionBatchStatusExitStatus를 갖는다.
StepContributionStepExecutionExitStatus 값을 변경할 수 있는 기능을 갖는다.
ExitStatus의 기본 종료 코드 외에 사용자 정의 종료 코드를 생성해서 적용할 수 있다.

아래는 StepContribution의 생성부터 청크 프로세스의 변경 사항을 StepExecution에 업데이트하는 과정을 나타낸 도식이다.

StepContribution 변경 사항 업데이트 이미지

ExecutionContext

ExecutionContext는 프레임워크에서 유지 및 관리하는 키/값으로 된 컬렉션으로, StepExecution 또는 JobExecution 객체의 상태를 저장하는 공유 객체이다.
ExecutionContext가 데이터베이스에 저장될 때 Json 형태로 직렬화 한 값으로 저장된다.

ExecutionContext가 공유되는 범위는 다음과 같다.

  • Step 범위 : 각 StepStepExecution에 저장되며 Step 간 서로 공유되지 않는다.
  • Job 범위 : 각 JobJobExecution에 저장되며 Job 간 서로 공유되지 않으나 해당 JobStep 간에는 서로 공유가 가능하다.

ExecutionContextJob 재시작시 이미 처리한 데이터는 건너뛰고 이후로 수행하도록 할 때 상태 정보를 활용한다.

ExecutionContext 이미지

Job이 실패하여 재실행 될 때, JobExecutionContext를 처음부터 새로 생성하는 것이 아니라 데이터베이스에 저장된 JobExecutionContext를 바탕으로 재생성하여 사용한다.
따라서 Job 실패 직전에 JobExecutionContext에 담아둔 데이터가 있다면, 해당 Job을 재수행하면 이전 Job에서 담아둔 데이터를 꺼내어 사용할 수 있다.

JobRepository

JobRepository는 배치 작업 중의 정보를 저장하는 저장소 역할을 한다.
Job이 언제 수행되었고, 언제 끝났는지, 몇 번 실행되었고 실행에 대한 결과 등의 배치 작업의 수행과 관련된 모든 메타 데이터를 저장한다.

JobRepository@EnableBatchProcessing 어노테이션을 선언하면 자동으로 빈으로 생성된다.
JobRepository를 커스터마이징 하려면 BatchConfigurer 인터페이스를 구현하거나 BasicBatchConfigurer를 상속하면 된다.

JobRepository 커스터마이징을 통해 트랜잭션 격리 수준 설정, 메타 데이터 테이블의 Prefix 변경 등과 같은 설정을 할 수 있다.

JobLaucher

JobLaucher는 배치 Job을 실행시키는 역할을 한다.
JobLaucner.run() 메서드는 JobJobParameters를 인자로 받으며 요청된 배치 작업을 수행한 후 클라이언트에게 JobExecution을 반환한다.

JobLaucher를 통한 Job의 실행은 동기적, 비동기적으로 실행되도록 설정할 수 있다.

Job이 동기적으로 실행되도록 하려면 JobLauchertaskExecutorSyncTaskExecutor로 설정한다(이는 기본값이 SyncTaskExecutor이다).
Job이 동기적으로 실행되면 JobLauncherJobExecution을 획득하고 배치 처리를 최종적으로 완료한 이후 클라이언트에게 JobExecution을 반환한다.

Job이 비동기적으로 실행되도록 하려면 JobLaunchertaskExecutorSimpleAsyncTaskExecutor로 설정한다.
비동기적으로 실행되면 JobLaucherJobExecution을 획득한 이후 클라이언트에게 바로 JobExecution을 반환하고, 이후 배치 처리를 완료한다.

기본적으로 스프링 배치에서는 JobLauncherApplicationRunner가 자동적으로 JobLauncher를 실행시킨다.
따라서 배치 어플리케이션 실행시, JobLauncherApplicationRunner에 의해 자동으로 Job이 실행된다.
spring.batch.job.enabled=false 옵션을 주어 자동적으로 실행하지 않도록 설정해야 Job의 실행을 직접 컨트롤 할 수 있다.

'framework > spring-batch' 카테고리의 다른 글

[Spring Batch] Job 관련 내용 정리  (0) 2022.12.19
[Spring Batch] 스프링 배치 기초, 메타 데이터와 도메인

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.