Appendix B: Meta-Data Schema

B.1. Overview

스프링 배치 메타데이터 테이블은 자바에서 이를 나타내는 도메인 객체와 밀접하다. 예를 들어 잡인스턴스(JobInstance), 잡익스큐션(JobExecution), 잡파라미터(JobParameters)스텝익스큐션(StepExecution)은 각각 BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTION, BATCH_JOB_EXECUTION_PARAMSBATCH_STEP_EXECUTION에 매핑된다. 익스큐션컨텍스트(ExecutionContext)BATCH_JOB_EXECUTION_CONTEXTBATCH_STEP_EXECUTION_CONTEXT에 매핑된다.

잡리포지터리(JobRepository)는 각 자바 객체를 올바른 테이블에 저장하는 일을 담당한다. 이 부록에서는 메타데이터 테이블을 생성할 때 내린 여러 설계 결정과 함께 메타데이터 테이블을 자세히 설명한다. 이 부록 뒷부분에 설명된 다양한 테이블 생성 명령문(creation statements)을 볼 때 일반적으로 사용되는 데이터 타입이라는 점을 유의하자. 스프링 배치는 예제로 많은 스키마를 제공한다. 개별 데이터베이스 공급업체가 데이터를 처리하는 방식이 있기 때문에 다양한 데이터 타입이 있다. 다음 이미지는 6개 테이블의 ERD 모델과 테이블 간의 관계를 보여준다:

Spring Batch Meta-Data ERD 이미지 35. 스프링 배치 메타데이터 ERD

B.1.1. Example DDL Scripts

스프링 배치 코어 JAR 파일에는 여러 데이터베이스 플랫폼에 대한 관계형 테이블을 생성하는 예제 스크립트가 포함되어 있다(이는, 잡 리포지터리 팩토리 빈 또는 동등한 네임스페이스에 의해 오토와이어드됨). 이런 스크립트는 그대로 사용하거나 원하는 대로 추가 인덱스(index) 및 제약 조건(constraint)을 사용하여 수정할 수 있다. 파일 이름은 Schema-*.sql 형식이다. 여기서 *는 대상 데이터베이스 플랫폼의 짧은 이름이다. 스크립트는 org.springframework.batch.core 패키지에 있다.

B.1.2. Migration DDL Scripts

스프링 배치 버전을 업그레이드할 때 실행해야 하는 마이그레이션 DDL 스크립트를 제공한다. 이 스크립트는 org/springframework/batch/core/migration 아래의 코어 Jar 파일에서 찾을 수 있다. 마이그레이션 스크립트는 도입된 버전 번호에 해당하는 폴더로 구성된다:

  • 2.2: 2.2 이전 버전에서 버전 2.2로 마이그레이션하는 데 필요한 스크립트가 포함되어 있다.
  • 4.1: 4.1 이전 버전에서 4.1 버전으로 마이그레이션하는 데 필요한 스크립트가 포함되어 있다.

B.1.3. Version

이 부록에서 설명하는 대부분의 데이터베이스 테이블에는 버전 컬럼이 포함되어 있다. 이 컬럼은 중요하다. 스프링 배치는 데이터베이스 업데이트를 처리할 때 낙관적 잠금 전략을 사용하기 때문이다. 이는 레코드가 “접촉”(업데이트)될 때마다 버전 컬럼의 값이 1씩 증가한다는 의미이다. 리포지터리가 값을 저장하기 위해 돌아갈 때, 버전 번호가 변경된 경우 동시 접근(concurrent)에 오류가 있음을 나타내는 옵티미스틱락킹페일러익셉션(OptimisticLockingFailureException)이 발생한다. 서로 다른 배치 잡이 서로 다른 시스템에서 실행되더라도, 모두 동일한 데이터베이스 테이블을 사용하므로 이것은 확인이 필요하다.

B.1.4. Identity

BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTIONBATCH_STEP_EXECUTION에는 각각 _ID로 끝나는 컬럼이 포함되어 있다. 이러한 필드는 해당 테이블의 프라이머리 키 역할을 한다. 그러나 데이터베이스의 생성 키는 아니다. 별도의 시퀀스로 생성된다. 이는 도메인 객체를 데이터베이스에 삽입한 후, 자바에서 고유하게 식별할 수 있도록 해당 객체에 제공된 키를 실제 객체에 설정해야 하기 때문에 필요하다. 최신 데이터베이스 드라이버(JDBC 3.0 이상)는 데이터베이스 생성 키를 사용하여 이 기능을 지원한다. 그러나, 해당 기능을 필요로하는 대신 시퀀스가 ​​사용된다. 스키마에는 다음 명령문의 일부 형태가 포함되어 있다:

    CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ;
    CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ;
    CREATE SEQUENCE BATCH_JOB_SEQ;

많은 데이터베이스 공급업체는 시퀀스를 지원하지 않는다. 이러한 경우 MySQL에 처럼 다음 명령문 같은 해결 방법이 있다:

    CREATE TABLE BATCH_STEP_EXECUTION_SEQ (ID BIGINT NOT NULL) type=InnoDB;
    INSERT INTO BATCH_STEP_EXECUTION_SEQ values(0);
    CREATE TABLE BATCH_JOB_EXECUTION_SEQ (ID BIGINT NOT NULL) type=InnoDB;
    INSERT INTO BATCH_JOB_EXECUTION_SEQ values(0);
    CREATE TABLE BATCH_JOB_SEQ (ID BIGINT NOT NULL) type=InnoDB;
    INSERT INTO BATCH_JOB_SEQ values(0);

위 경우에는, 각 시퀀스 대신 테이블이 사용된다. 스프링 코어 클래스인 MySQL맥스벨류인크리먼터(MySQLMaxValueIncrementer)는 비슷한 기능을 제공하기 위해, 이 시퀀스에서 하나의 컬럼을 증가시킨다.

B.2. The BATCH_JOB_INSTANCE Table

BATCH_JOB_INSTANCE 테이블은 잡인스턴스(JobInstance)와 관련된 모든 정보를 보유하고 전체 계층 구조의 최상위이다. 이를 생성하는 데 다음 일반 DDL 문이 사용된다:

    CREATE TABLE BATCH_JOB_INSTANCE  (
        JOB_INSTANCE_ID BIGINT  PRIMARY KEY ,
        VERSION BIGINT,
        JOB_NAME VARCHAR(100) NOT NULL ,
        JOB_KEY VARCHAR(32) NOT NULL
    );

다음 리스트는 테이블의 각 컬럼을 설명한다:

  • JOB_INSTANCE_ID: 인스턴스를 식별하는 고유 ID이다. 프라이머리 키다. 이 컬럼의 값은 잡인스턴스(JobInstance)에서 getId 메소드를 호출하여 얻을 수 있어야 한다.
  • VERSION: 버전을 보자.
  • JOB_NAME: 잡(Job) 객체에서 얻은 잡명이다. 인스턴스를 식별하는 데 필요하므로 not null이어야 한다.
  • JOB_KEY: 동일 잡의 개별 인스턴스를 서로 고유하게 식별하는 잡파라미터(JobParametes)의 직렬화이다 (동일한 작업 이름을 가진 잡인스턴스(JobInstances)잡파라미터(JobParameters)가 달라야 하므로 JOB_KEY 값도 달라야 한다.)

B.3. The BATCH_JOB_EXECUTION_PARAMS Table

BATCH_JOB_EXECUTION_PARAMS 테이블은 잡파라미터(JobParameters) 객체와 관련된 모든 정보를 보유하고 있다. 잡에 전달된 0개 이상의 키/값 쌍을 포함하며 이 실행되는 파라미터의 기록 역할을 한다. 잡 ID 생성에 기여하는 각 파라미터에 대해 IDENTIFYING 플래그가 true로 설정된다. 테이블은 비정규화되어있다. 각 파라미터 타입에 대해 별도의 테이블을 생성하는 대신 다음과 같이 타입 컬럼이 포함된 테이블이 있다:

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

다음은 각 컬럼에 대한 설명이다:

  • JOB_EXECUTION_ID: 파라미터의 각 아이템이 속한 잡 익스큐션을 나타내는 BATCH_JOB_EXECUTION 테이블의 외래 키(Foreign key)다. 각 익스큐션마다 여러 로우(즉, 키/값 쌍)가 존재할 수 있다.
  • PARAMETER_NAME: 파라미터명
  • PARAMETER_TYPE: 파라미터 타입명
  • PARAMETER_VALUE: 파라미터 값
  • IDENTIFYING: 파라미터가 관련 잡인스턴스(JobInstance) 식별(identity)에 기여했는지 여부를 나타내는 플래그이다.

이 테이블에는 프라이머리 키가 없다. 이는 프레임워크가 사용하지 않기 때문이다. 필요한 경우 프레임워크 자체에 문제를 일으키지 않고, 데이터베이스 생성 키(generated key)를 사용하여 프라이머리 키를 추가할 수 있다.

B.4. The BATCH_JOB_EXECUTION Table

BATCH_JOB_EXECUTION 테이블은 잡익스큐션(JobExecution) 객체와 관련된 모든 정보를 보유하고 있다. 잡이 실행될 때마다, 항상 잡익스큐션(JobExecution)이 있고 테이블에 새 로우로 저장되어 있다. 다음은 BATCH_JOB_EXECUTION 테이블의 정의를 보여준다:

    CREATE TABLE BATCH_JOB_EXECUTION  (
        JOB_EXECUTION_ID BIGINT  PRIMARY KEY,
        VERSION BIGINT,
        JOB_INSTANCE_ID BIGINT NOT NULL,
        CREATE_TIME TIMESTAMP NOT NULL,
        START_TIME TIMESTAMP DEFAULT NULL,
        END_TIME TIMESTAMP DEFAULT NULL,
        STATUS VARCHAR(10),
        EXIT_CODE VARCHAR(20),
        EXIT_MESSAGE VARCHAR(2500),
        LAST_UPDATED TIMESTAMP,
        constraint JOB_INSTANCE_EXECUTION_FK foreign key (JOB_INSTANCE_ID)
        references BATCH_JOB_INSTANCE(JOB_INSTANCE_ID)  
    );

다음은 각 컬럼에 대한 설명이다:

  • JOB_EXECUTION_ID: 이 익스큐션을 고유하게 식별하는 프라이머리 키다. 이 컬럼의 값은 잡익스큐션(JobExecution) 객체의 getId 메소드를 호출하여 얻을 수 있다.
  • VERSION: 버전을 보자.
  • JOB_INSTANCE_ID: BATCH_JOB_INSTANCE 테이블의 외래 키다. 이 익스큐션이 속한 인스턴스를 나타낸다. 인스턴스당 둘 이상의 익스큐션이 있을 수 있다.
  • CREATE_TIME: 익스큐션이 생성된 시간을 나타내는 타임스탬프이다.
  • START_TIME: 익스큐션이 시작된 시간을 나타내는 타임스탬프이다.
  • END_TIME: 성공 또는 실패에 관계없이 익스큐션이이 완료된 시간을 나타내는 타임스탬프이다. 잡이 현재 실행 중이 아닐 때, 이 컬럼의 값이 비어 있으면 특정 타입의 오류가 발생하여 프레임워크가 실패하기 전에 마지막 저장을 수행할 수 없음을 나타낸다.
  • STATUS: 익스큐션 상태를 나타내는 문자열이다. COMPLETED, STARTED 등이 있을 수 있다. 이 컬럼의 객체 표현은 배치스테이터스(BatchStatus) 이넘(enumeration)이다.
  • EXIT_CODE: 익스큐션 종료 코드를 나타내는 문자열이다. 커맨드라인의 경우 숫자로 변환될 수 있다.
  • EXIT_MESSAGE: 잡이 종료된 방법에 대한 자세한 설명을 나타내는 문자열이다. 실패할 경우 여기에는 가능한 한 많은 스택 트레이스(stack trace)가 포함될 수 있다.
  • LAST_UPDATED: 익스큐션이 마지막으로 저장된 시간을 나타내는 타임스탬프이다.

B.5. The BATCH_STEP_EXECUTION Table

BATCH_STEP_EXECUTION 테이블은 스텝익스큐션(StepExecution) 객체와 관련된 모든 정보를 보유하고 있다. 이 테이블은 여러 면에서 BATCH_JOB_EXECUTION 테이블과 유사하며 생성된 각 잡익스큐션(JobExecution)에 대해 스텝당 아이템이 항상 하나 이상 있다. 다음은 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,
        CREATE_TIME TIMESTAMP NOT NULL,
        START_TIME TIMESTAMP DEFAULT NULL ,
        END_TIME TIMESTAMP 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(20) ,
        EXIT_MESSAGE VARCHAR(2500) ,
        LAST_UPDATED TIMESTAMP,
        constraint JOB_EXECUTION_STEP_FK foreign key (JOB_EXECUTION_ID)
        references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
    );

다음은 각 컬럼을 설명한다:

  • STEP_EXECUTION_ID: 이 익스큐션을 고유하게 식별하는 프라이머리 키다. 이 컬럼의 값은 스텝익스큐션(StepExecution) 객체의 getId 메소드를 호출하여 얻을 수 있다.
  • VERSION: 버전을 보자.
  • STEP_NAME: 이 익스큐션이 속한 스텝명이다.
  • JOB_EXECUTION_ID: BATCH_JOB_EXECUTION 테이블의 외래 키다. 스텝익스큐션(StepExecution)이 속한 잡익스큐션(JobExecution)을 나타낸다. 주어진 잡익스큐션(JobExecution)에 대한 스텝익스큐션(StepExecution)의 스텝명은 하나만 있을 수 있다.
  • START_TIME: 익스큐션이 시작된 시간을 나타내는 타임스탬프이다.
  • END_TIME: 성공 또는 실패에 관계없이 익스큐션이 완료된 시간을 나타내는 타임스탬프이다. 잡이 현재 실행되고 있지 않더라도 이 컬럼의 값이 비어 있으면, 특정 타입의 오류가 발생하여 프레임워크가 실패하기 전 마지막 저장을 수행할 수 없음을 나타낸다.
  • STATUS: 실행 상태를 나타내는 문자열이다. COMPLETED, STARTED 등이 있을 수 있다. 이 컬럼의 객체 표현은 배치스테이터스(BatchStatus) 이넘(enumeration)이다.
  • COMMIT_COUNT: 이 익스큐션에서 스텝이 트랜잭션을 커밋한 횟수다.
  • READ_COUNT: 이 익스큐션에서 읽은 아이템 수다.
  • FILTER_COUNT: 이 익스큐션에서 필터링된 항목 수다.
  • WRITE_COUNT: 이 익스큐션에서 작성(written)되고 커밋된 아이템 수다.
  • READ_SKIP_COUNT: 이 익스큐션에서 읽기 시 스킵한 아이템의 수다.
  • WRITE_SKIP_COUNT: 이 익스큐션에서 쓰기 시 스킵한 아이템 수다.
  • PROCESS_SKIP_COUNT: 이 익스큐션에서 처리 중에 스킵한 아티템 수다.
  • ROLLBACK_COUNT:이 익스큐션에서 롤백 횟수다. 이 수는 재시도 롤백 및 스킵 복구 절차의 롤백을 포함하여, 롤백이 발생할 때마다 증가한다.
  • EXIT_CODE: 실행 종료 코드를 나타내는 문자열이다. 커맨트라인 잡의 경우 숫자로 변환될 수 있다.
  • EXIT_MESSAGE: 잡이 종료된 방법에 대한 자세한 설명을 나타내는 문자열이다. 실패 시 여기에 가능한 한 많은 스택 트레이싱이 포함될 수 있다.
  • LAST_UPDATED: 이 익스큐션이 마지막으로 저장된 시간을 나타내는 타임스탬프다.

B.6. The BATCH_JOB_EXECUTION_CONTEXT Table

BATCH_JOB_EXECUTION_CONTEXT 테이블은 잡의 익스큐션컨텍스트(ExecutionContext)와 관련된 모든 정보를 보유하고 있다. 각 잡익스큐션(JobExecution)에는 정확히 하나의 잡에 익스큐션컨텍스트(ExecutionContext)가 있으며 여기에는 특정 잡 익스큐션에 필요한 모든 잡 레벨 데이터가 포함되어 있다. 이 데이터는 일반적으로 잡인스턴스(JobInstance)가 “중단된 지점에서 시작”할 수 있도록 실패 후 검색해야 하는 상태를 나타냅니다. 다음은 BATCH_JOB_EXECUTION_CONTEXT 테이블의 정의를 보여준다:

    CREATE TABLE BATCH_JOB_EXECUTION_CONTEXT  (
        JOB_EXECUTION_ID BIGINT PRIMARY KEY,
        SHORT_CONTEXT VARCHAR(2500) NOT NULL,
        SERIALIZED_CONTEXT CLOB,
        constraint JOB_EXEC_CTX_FK foreign key (JOB_EXECUTION_ID)
        references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
    );

다음은 각 컬럼에 대한 설명이다:

  • JOB_EXECUTION_ID: 컨텍스트가 속한 잡익스큐션(JobExecution)을 나타내는 외래 키다. 특정 익스큐션과 관련된 로우가 두 개 이상 있을 수 있다.
  • SHORT_CONTEXT: SERIALIZED_CONTEXT의 문자열 버전.
  • SERIALIZED_CONTEXT: 직렬화된 전체 컨텍스트.

B.7. The BATCH_STEP_EXECUTION_CONTEXT Table

BATCH_STEP_EXECUTION_CONTEXT 테이블은 스텝의 익스큐션컨텍스트(ExecutionContext)와 관련된 모든 정보를 보유하고 있다. 스텝익스큐션(StepExecution) 당 정확히 하나의 익스큐션컨텍스트(ExecutionContext)가 있으며 여기에는 특정 스텝 익스큐션을 위해 유지되어야 하는 모든 데이터가 포함되어 있다. 이 데이터는 잡인스턴스(JobInstance)가 “중단된 지점에서 시작”할 수 있도록 실패 후 검색되어야하는 상태를 나타낸다. 다음은 BATCH_STEP_EXECUTION_CONTEXT 테이블의 정의를 보여준다:

    CREATE TABLE BATCH_STEP_EXECUTION_CONTEXT  (
        STEP_EXECUTION_ID BIGINT PRIMARY KEY,
        SHORT_CONTEXT VARCHAR(2500) NOT NULL,
        SERIALIZED_CONTEXT CLOB,
        constraint STEP_EXEC_CTX_FK foreign key (STEP_EXECUTION_ID)
        references BATCH_STEP_EXECUTION(STEP_EXECUTION_ID) 
    );

다음은 각 컬럼에 대한 설명이다:

  • STEP_EXECUTION_ID: 컨텍스트가 속한 스텝익스큐션(StepExecution)을 나타내는 외래 키다. 특정 익스큐션과 관련된 로우가 두 개 이상 있을 수 있다.
  • SHORT_CONTEXT: SERIALIZED_CONTEXT의 문자열 버전.
  • SERIALIZED_CONTEXT: 직렬화된 전체 컨텍스트.

B.8. Archiving

배치 잡이 실행될 때마다 여러 테이블에 아이템이 있기 때문에, 일반적으로 메타데이터 테이블에 대한 보관 전략을 만들어 둔다. 테이블 자체는 과거에 발생한 일에 대한 기록을 나타내도록 설계되었으며, 일반적으로 재시작과 관련된 몇 가지 주목할만한 예외를 제외하고는 잡 실행에 영향을 미치지 않는다:

  • 프레임워크는 메타데이터 테이블을 사용하여 특정 잡인스턴스(JobInstance)가 이전에 실행됐었는지 여부를 확인한다. 잡이 실행되었지만 잡을 재시작할 수 없으면 예외가 발생한다.
  • 잡인스턴스(JobInstance)에 대한 아이템이 성공적으로 완료되지 않은 채 제거되면, 프레임워크는 해당 잡을 재시작하는 것이 아니라 새로운 것으로 간주한다.
  • 잡이 재시작되면 프레임워크는 익스큐션컨텍스트(ExecutionContext)에 유지된 모든 데이터를 사용하여 잡의 상태를 복원한다. 따라서 성공적으로 완료되지 않은 잡의 테이블에서 항목을 제거하면 해당 잡이 재실행될 때 올바른 지점에서 시작되지 않는다.

B.9. International and Multi-byte Characters

비즈니스 처리에 멀티바이트 문자 세트(예: 중국어 또는 키릴 문자)를 사용하는 경우 해당 문자를 스프링 배치 스키마에 저장해야 할 수도 있다. 대부분의 사용자는 단순히 스키마를 변경하여 VARCHAR 컬럼 길이를 두 배로 늘리는 것만으로도 충분하다고 생각한다. 다른 사람들은 VARCHAR 컬럼 길이 값의 절반인 max-varchar-length잡리포지터리(JobRepository)를 구성하는 것을 선호한다. 일부 사용자는 스키마 정의에서 VARCHAR 대신 NVARCHAR을 사용한다고 보고했다. 최상의 결과는 데이터베이스 플랫폼과 데이터베이스 서버의 로컬 구성 방식에 따라 달라진다.

B.10. Recommendations for Indexing Metadata Tables

스프링 배치는 여러 데이터베이스 플랫폼에 대한 핵심 jar 파일에서 메타데이터 테이블에 대한 DDL 샘플을 제공한다. 인덱스 선언은 해당 DDL에 포함되지 않는다. 정확한 플랫폼, 현지 규칙 및 잡 운영 방식에 대한 비즈니스 요구 사항에 따라 사용자가 인덱스를 생성하는 방법이 너무 다양하기 때문이다. 다음 테이블은 스프링 배치에서 제공하는 DAO 구현체에 의해 WHERE 절에서 어떤 컬럼이 사용될 것인지와 개별 프로젝트에서 인덱싱을 스스로 결정할 수 있게 얼마나 자주 사용하는지에 대한 몇 가지 예제를 제공한다:

테이블 21. SQL 문의 Where 절(프라이머리 키 제외) 및 대략적인 사용 빈도

기본 테이블명Where 절빈도
BATCH_JOB_INSTANCEJOB_NAME = ? and JOB_KEY = ?잡이 시작될 때마다
BATCH_JOB_EXECUTIONJOB_INSTANCE_ID = ?잡이 다시 시작될 때마다
BATCH_STEP_EXECUTIONVERSION = ?커밋 간격, 즉 청크(그리고 스텝의 시작과 끝)
BATCH_STEP_EXECUTIONSTEP_NAME = ? and JOB_EXECUTION_ID = ?각 스텝 실행 전