이전글에서 간단하게 Spring Batch가 수행되게 구현해보았습니다.
JobRepositoy
db또는 메모리에 스프링배치가 실행할 수 있도록 배치 메타데이터로 관리하는 클래스입니다.
메타데이터는 나중에 더 자세히 알아보겠습니다. 여기에 이어지는 글입니다.
메타 데이터란, 데이터를 설명하는 데이터라고 보시면 됩니다.
위키피디아보다 나무위키가 더 설명이 잘되어있어 나무위키 링크를 첨부합니다
Spring Batch의 메타 데이터는 다음과 같은 내용들을 담고 있습니다.
- 이전에 실행한 Job이 어떤 것들이 있는지
- 최근 실패한 Batch Parameter가 어떤것들이 있고, 성공한 Job은 어떤것들이 있는지
- 다시 실행한다면 어디서 부터 시작하면 될지
- 어떤 Job에 어떤 Step들이 있었고, Step들 중 성공한 Step과 실패한 Step들은 어떤것들이 있는지
등등 Batch 어플리케이션을 운영하기 위한 메타데이터가 여러 테이블에 나눠져 있습니다.
메타 테이블 구조는 아래와 같습니다.
![[Batch] 메타데이터, 메타 테이블 [Batch] 메타데이터, 메타 테이블](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
사진 출처: 스프링배치 문서
위와 같은 테이블들을 메타 테이블이라고 합니다. 메타테이블에대해 알아 보겠습니다.
- 배치 실행을 위한 메타 데이터가 저장되는 테이블
- BATCH_JOB_INSTANCE
- Job이 실행되며 생성되는 최상위 계층의 테이블
- job_name과 job_key를 기준으로 하나의 row가 생성되며, 같은 job_name과 job_key가 저장될 수 없다.
- job_key는 BATCH_JOB_EXECUTION_PARAMS에 저장되는 Parameter를 나열해 암호화해 저장한다.
- BATCH_JOB_EXECUTION
- Job이 실행되는 동안 시작/종료 시간, job 상태 등을 관리
- BATCH_JOB_EXECUTION_PARAMS
- Job을 실행하기 위해 주입된 parameter 정보 저장
- BATCH_JOB_EXECUTION_CONTEXT
- Job이 실행되며 공유해야할 데이터를 직렬화해 저장
- BATCH_STEP_EXECUTION
- Step이 실행되는 동안 필요한 데이터 또는 실행된 결과 저장
- BATCH_STEP_EXECUTION_CONTEXT
- Step이 실행되며 공유해야할 데이터를 직렬화해 저장
이 메타데이터의 스크립트는 spring-batch-core/org.springframework/batch/core/* 하위에 위치하고 있습니다.
- 스프링 배치를 실행하고 관리하기 위한 테이블
- schema.sql 설정
- schema-**.sql의 실행 구분은
- spring.batch.initialize-schema config로 구분한다.
- ALWAYS, EMBEDDED, NEVER로 구분한다.
- ALWAYS : 항상 실행
- EMBEDDED : 내장 DB일 때만 실행
- NEVER : 항상 실행 안함
- 기본 값은 EMBEDDED다.
![[Batch] 메타데이터, 메타 테이블 [Batch] 메타데이터, 메타 테이블](http://t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png)
저는 mysql을 사용하기 때문에 schema-mysql.sql을 열어준뒤에 테이블을 생성해주도록 하겠습니다.
schema-mysql.sql
-- auto-generated definition
-- No source text available
-- 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),
JOB_CONFIGURATION_LOCATION VARCHAR(2500) NULL,
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 ,
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)
) 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,
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)
) 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);
![[Batch] 메타데이터, 메타 테이블 - schema-mysql.sql [Batch] 메타데이터, 메타 테이블 - schema-mysql.sql](https://blog.kakaocdn.net/dn/Odhnt/btrIsRGin7g/udAMubSpg7tvArbhWpVg60/img.png)
이런식으로 테이블이 생성이 잘 되었습니다. 그리고 application.yml에서 스크립트 생성 시점을 설정할 수 있습니다.
application.yml
![[Batch] 메타데이터, 메타 테이블 - application.yml [Batch] 메타데이터, 메타 테이블 - application.yml](https://blog.kakaocdn.net/dn/cSzTPG/btrImPXvGV8/8tsaIrMFbWcXSecRT7Bid0/img.png)
개발 환경에서는 always 나 embeded를 하지만 운영 환경에서는 never로 설정해두는것을 권장한다고 합니다.
운영환경에서 스프링 배치에 의해서 스크립트가 자동으로 생성된다는 의미는 sql의 ddl이 운영데이터베이스에 직접 실행된다는 의미 입니다. 그래서 위험합니다.
이제 각 테이블들의 역할을 알아보겠습니다.
- JobInstance : BATCH_JOB_INSTANCE 테이블과 매핑
- JobExecution : BATCH_JOB_EXECUTION 테이블과 매핑
- JobParameters : BATCH_JOB_EXECUTION_PARAMS 테이블과 매핑
- ExecutionContext : BATCH_JOB_EXECUTION_CONTEXT 테이블과 매핑
![[Batch] 메타데이터, 메타 테이블 - application.yml [Batch] 메타데이터, 메타 테이블 - application.yml](https://blog.kakaocdn.net/dn/bDhnL3/btrInIqiJNq/kWyOQyyHFvQiWXfURNTYj0/img.png)
JobInstance의 생성기준은 JobName과 Jobkey와 입니다.
- JobInstance의 생성 기준은 JobParamters 중복 여부에 따라 생성된다.
- 다른 parameter로 Job이 실행되면, JobInstance가 생성된다.
- 같은 parameter로 Job이 실행되면, 이미 생성된 JobInstance가 실행된다.
- JobExecution은 항상 새롭게 생성된다.
- 예를 들어
- 음 Job 실행 시 date parameter가 1월1일로 실행 됐다면, 1번 JobInstance가 생성된다.
- 다음 Job 실행 시 date parameter가 1월2일로 실행 됐다면, 2번 JobInstance가 생성된다.
- 다음 Job 실행 시 date parameter가 1월2일로 실행 됐다면, 2번 JobInstance가 재 실행된다.
- 이때 Job이 재실행 대상이 아닌 경우 에러가 발생한다.
- Parameter가 없는 Job을 항상 새로운 JobInstance가 실행되도록 RunIdIncrementer가 제공된다.
근데 저번 글에서 만든 Job은 재실행대상이라고 지정하지 않았는데 어떻게 에러가 나지 않고 새로운 Job을 실행했을까요.
@Bean
public Job helloJob() {
return jobBuilderFactory.get("helloJob")
.incrementer(new RunIdIncrementer())
.start(this.helloStep())
.build();
}
new RunIdIncrementer() 클래스는 새로운 잡 인스턴스를 시퀀스방법으로 만들도록 해줍니다. RunId라는 파라미터값을 내부에서 자동으로 생성해서 이 클래스없이 실행되면 매번 동일한 잡으로 실행된다는 의미입니다.
이 설정을 추가하면 항상 다른 잡인스턴스를 실행된다고 생각해야합니다.
hellojob을 실행하면 새로운 RunId가 BATCH_JOB_EXECUTION_PARAMS 테이블에 인서트 되는지 확인해 보겠습니다.
appication-mysql.yml
spring:
datasource:
hikari:
jdbc-url: jdbc:mysql://127.0.0.1:3307/spring_batch?characterEncoding=UTF-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: jisu
password: 1234
batch:
initialize-schema: never
제 컴퓨터는 mariaDB가 3306 포트를 사용중이여서 mysql은 3307포트를 사용중입니다. initialize-schema 은 never로 설정 해줍니다.
그리고 오른쪽 상단에 Edit configurations을 눌러줍니다.
![[Batch] 메타데이터, 메타 테이블 - appication-mysql.yml [Batch] 메타데이터, 메타 테이블 - appication-mysql.yml](https://blog.kakaocdn.net/dn/uQq1U/btrIsSd41SO/qFgjxqYPJVy4kOo7LhXkp1/img.png)
Active profies 에 mysql을 적어 줍니다. 이렇게 되면 스프링부트는 application의 mysql.yml 파일(application-mysql.yml)을 자동으로 읽어서 프로젝트를 시작하게 됩니다.
프로젝트를 실행해보겠습니다.
![[Batch] 메타데이터, 메타 테이블 - appication-mysql.yml [Batch] 메타데이터, 메타 테이블 - appication-mysql.yml](https://blog.kakaocdn.net/dn/Fsoz9/btrInIw7Is1/TZWh2RcoXKmbkxi3zt3xmK/img.png)
hello spring batch 가 잘 나오고 프로젝트가 잘 실행되었습니다.
그리고 다시 한번 실행을 시켜보겠습니다.
select *
from batch_job_execution_params;
그리고 위와 같이 테이블을 조회해봅시다.
![[Batch] 메타데이터, 메타 테이블 - appication-mysql.yml [Batch] 메타데이터, 메타 테이블 - appication-mysql.yml](https://blog.kakaocdn.net/dn/cJDevX/btrIq5ZcbSB/1JZIoNfhZOiE5M6Hw7wo91/img.png)
두번 실행 했으므로 key_name의 run_id가 두번 생성되었고 LONG_VAL이 1,2 로 서로 다른 값으로 인서트 된것을 확인할 수 있습니다.
정리
잡 인스턴스의 생성기준 : 잡 이름과 잡파라미터의 값을 기준으로 잡인스턴스가 실행되느냐 재실행되느냐, 재실행의 실패하느냐 결정이 됩니다. 하나의 잡은 항상 같은 파라미터로 실행될수 없습니다.
ExcutionContext는 잡과 스텝의 컨텍스트를 관리하는 객체입니다. 이 객체를 통하여 데이터를 서로 공유 할 수있습니다.
'Spring Boot > Batch' 카테고리의 다른 글
[Batch] ItemWriter 데이터 쓰기 (0) | 2022.08.08 |
---|---|
[Batch] ItemReader (JDBC,JPA) (0) | 2022.08.08 |
[Batch] Execution 데이터 공유 (0) | 2022.07.28 |
[Batch] 스프링 배치 시작하기 (0) | 2022.07.28 |
댓글