Appendix A: 元数据 Schema

本站( springdoc.cn )中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。

概览

Spring 批处理元数据表与 Java 中表示它们的 domain 对象非常匹配。例如,JobInstanceJobExecutionJobParametersStepExecution 分别映射到 BATCH_JOB_INSTANCEBATCH_JOB_EXECUTIONBATCH_JOB_EXECUTION_PARAMSBATCH_STEP_EXECUTIONExecutionContext 映射到 BATCH_JOB_EXECUTION_CONTEXTBATCH_STEP_EXECUTION_CONTEXTJobRepository 负责将每个 Java 对象保存和存储到正确的表中。本附录详细描述了元数据表以及创建这些表时的许多设计决策。在查看本附录后面描述的各种表创建语句时,请注意所使用的数据类型尽可能通用。Spring Batch 提供了许多 schema 作为示例。由于各数据库供应商处理数据类型的方式不同,因此所有 schema 的数据类型也各不相同。下图显示了所有六个表及其相互关系的 ERD 模型:

Spring Batch Meta-Data ERD
Figure 1. Spring Batch Meta-Data ERD

DDL 脚本示例

Spring Batch Core JAR 文件包含为多个数据库平台创建关系表的示例脚本(反过来,job repository 工厂 Bean 或命名空间等同物会自动检测这些平台)。这些脚本可按原样使用,也可根据需要使用附加索引和约束进行修改。文件名的格式为 schema-*.sql,其中 * 是目标数据库平台的简称。这些脚本位于 org.springframework.batch.core 包中。

迁移 DDL 脚本

Spring Batch 提供了升级版本时需要执行的迁移 DDL 脚本。这些脚本可以在 org/springframework/batch/core/migration 下的 Core Jar 文件中找到。迁移脚本按照引入时的版本号归类到相应的文件夹中:

  • 2.2:包含从 2.2 之前的版本迁移到 2.2 版本所需的脚本

  • 4.1:包含从 4.1 之前的版本迁移到 4.1 版本所需的脚本

版本

本附录中讨论的许多数据库表都包含 version 列。这一列非常重要,因为 Spring Batch 在处理数据库更新时采用了乐观锁。这意味着每当记录被更新一次,version 列中的值就会递增一次。当 repository 返回保存值时,如果 version 发生了变化,就会抛出 OptimisticLockingFailureException 异常,表明并发访问发生了错误。这种检查是必要的,因为尽管不同的批处理作业可能在不同的机器上运行,但它们都使用相同的数据库表。

ID

BATCH_JOB_INSTANCEBATCH_JOB_EXECUTIONBATCH_STEP_EXECUTION 各包含以 _ID 结尾的列。这些字段是各自表的主键。不过,它们不是数据库生成的键。相反,它们是由单独的序列生成的。这样做是必要的,因为在将其中一个 domain 对象插入数据库后,需要在实际对象上设置给定的键,以便在 Java 中对它们进行唯一标识。较新的数据库驱动程序(JDBC 3.0 及以上版本)支持使用数据库生成的键来实现这一功能。不过,我们并不要求这种功能,而是使用序列。schema 的每种变化都包含以下语句的某种形式:

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

在前一种情况中,使用了一个表来代替每个序列。然后,Spring 核心类 MySQLMaxValueIncrementer 会递增该序列中的一列,从而提供类似的功能。

BATCH_JOB_INSTANCE

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 对象中获取的 job 的名称。因为需要用它来标识实例,所以它不能为空。

  • JOB_KEY: JobParameters 的序列化,用于唯一标识同一 job 的不同实例。(有相同 job 名称的 JobInstances 必须具有不同的 JobParameters,从而具有不同的 JOB_KEY 值)。

BATCH_JOB_EXECUTION_PARAMS

BATCH_JOB_EXECUTION_PARAMS 表保存与 JobParameters 对象相关的所有信息。它包含传递给 Job 的 0 个或多个键/值对,可作为 job 运行时的参数记录。对于每个有助于生成 job 标识的参数,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_IDBATCH_JOB_EXECUTION 表中的外键,表示参数条目所属的 job execution。请注意,每个 execution 可能存在多行(即键/值对)。

  • PARAMETER_NAME:参数名称

  • PARAMETER_TYPE:参数类型的全称。

  • PARAMETER_VALUE:参数值。

  • IDENTIFYING:标志表示参数是否对相关的 JobInstance 的 id 有 contributed。

请注意,该表没有主键。这是因为框架不使用主键,因此不需要主键。如果需要,可以使用数据库自增key来添加主键,而不会对框架本身造成任何问题。

BATCH_JOB_EXECUTION

BATCH_JOB_EXECUTION 表保存与 JobExecution 对象相关的所有信息。每次运行一个 Job 时,该表中总会有一个新的被称为 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:唯一标识此 execution 的主键。此列的值可通过调用 JobExecution 对象的 getId 方法获得。

  • VERSION:见 版本

  • JOB_INSTANCE_IDBATCH_JOB_INSTANCE 表中的外键。它表示该 execution 所属的实例。每个实例可能有多个 execution。

  • CREATE_TIME:时间戳,代表创建 execution 的时间。

  • START_TIME:时间戳,代表开始执行的时间。

  • END_TIME:时间戳,代表 execution 结束的时间,无论成功或失败。当 job 当前未运行时,此列中的空值表示发生了某种类型的错误,框架无法在失败前执行最后一次保存。

  • STATUS:表示执行状态的字符串。可以是 COMPLETEDSTARTED 等。该列的对象表示是 BatchStatus 枚举。

  • EXIT_CODE:表示执行退出代码的字符串。如果是命令行任务,可将其转换为数字。

  • EXIT_MESSAGE:字符串,代表对任务如何退出的更详细描述。在失败的情况下,这可能包括尽可能多的堆栈跟踪。

  • LAST_UPDATED:时间戳,代表最后一次持久化该 execution 的时间。

BATCH_STEP_EXECUTION

BATCH_STEP_EXECUTION 表保存与 StepExecution 对象相关的所有信息。该表在很多方面都与 BATCH_JOB_EXECUTION 表相似,每个 Step 创建的每个 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:该 execution 所属 step 的名称。

  • JOB_EXECUTION_ID:来自 BATCH_JOB_EXECUTION 表的外键。它表示此 StepExecution 所属的 JobExecution。对于给定 Step 名称的给定 JobExecution,只能有一个 StepExecution

  • START_TIME:时间戳,代表开始执行的时间。

  • END_TIME:时间戳,代表执行结束的时间,无论成功或失败。如果该列中的值为空,即使 job 当前未在运行,也表示发生了某种类型的错误,框架无法在失败前执行最后一次保存。

  • STATUS:表示执行状态的字符串。可以是 COMPLETEDSTARTED 等。该列的对象表示是 BatchStatus 枚举。

  • COMMIT_COUNT:该 step 在本次执行过程中提交事务的次数。

  • READ_COUNT:本次执行过程中读取的项目(item)数。

  • FILTER_COUNT:本次执行过滤掉的项目数。

  • WRITE_COUNT:本次执行过程中写入和提交的项目数。

  • READ_SKIP_COUNT:本次执行过程中读取时跳过的项目数。

  • WRITE_SKIP_COUNT:本次执行过程中跳过写入的项目数。

  • PROCESS_SKIP_COUNT:本次执行过程中跳过处理的项目数。

  • ROLLBACK_COUNT:本次执行过程中的回滚次数。请注意,该计数包括每次回滚,包括重试回滚和跳过恢复过程中的回滚。

  • EXIT_CODE:表示执行退出代码的字符串。如果是命令行任务,可将其转换为数字。

  • EXIT_MESSAGE:字符串,代表对任务如何退出的更详细描述。在失败的情况下,这可能包括尽可能多的堆栈跟踪。

  • LAST_UPDATED:时间戳,代表最后一次持久化该执行的时间。

BATCH_JOB_EXECUTION_CONTEXT

BATCH_JOB_EXECUTION_CONTEXT 表保存与 Job ExecutionContext 相关的所有信息。每个 JobExecution 都有一个 Job ExecutionContext ,它包含特定 job 执行所需的所有 job 级数据。这些数据通常代表 job 失败后必须检索的状态,以便 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:外键,代表 context 所属的 JobExecution。与给定 execution 相关联的行可能不止一条。

  • SHORT_CONTEXTSERIALIZED_CONTEXT 的字符串版本。

  • SERIALIZED_CONTEXT:序列化的整个 context。

BATCH_STEP_EXECUTION_CONTEXT

BATCH_STEP_EXECUTION_CONTEXT 表保存与 StepExecutionContext 相关的所有信息。每个 StepExecution 都有一个 ExecutionContext,它包含特定 step execution 需要持久化的所有数据。这些数据通常代表失败后必须检索的状态,以便 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:外键,代表 context 所属的 StepExecution。与特定 execution 关联的行可能不止一条。

  • SHORT_CONTEXTSERIALIZED_CONTEXT 的字符串版本。

  • SERIALIZED_CONTEXT:序列化的整个 context。

归档

由于每次运行批处理作业(job)时都会在多个表中出现条目,因此通常会为元数据表创建归档策略。这些表本身的目的是显示过去发生的记录,通常不会影响任何作业的运行,但与重启有关的几个明显例外除外。

  • 该框架使用元数据表来确定特定的 JobInstance 之前是否运行过。如果该实例已被运行过,且该 job 不可重启,则会出现异常。

  • 如果某个 JobInstance 的条目在未成功完成的情况下被删除,框架会认为该 job 是新的,而不是重新启动的。

  • 如果重新启动 job,框架会使用已持久化到 ExecutionContext 的任何数据来恢复作业的状态。因此,如果从该表中删除未成功完成的 Job 的任何条目,就会阻止它们在再次运行时从正确的位置启动。

国际化和多字节字符

如果你在业务处理中使用多字节字符集(如中文或西里尔文),可能需要在 Spring Batch 模式中持久化这些字符。许多用户发现,只需将模式更改为 VARCHAR 列长度的两倍就足够了。其他用户则更喜欢在配置 JobRepository 时使用 max-varchar-lengthVARCHAR 列长度一半的值。一些用户还报告说,他们在 schema 定义中使用 NVARCHAR 代替 VARCHAR。最佳结果取决于数据库平台和本地配置数据库服务器的方式。

元数据表索引建议

Spring Batch 为核心 jar 文件中的元数据表提供了 DDL 样本,适用于几种常见的数据库平台。该 DDL 中不包括索引声明,因为用户可能希望如何建立索引,这取决于他们的精确平台、本地约定和作业操作的业务要求,变化太多了。下表提供了一些指示,说明 Spring Batch 提供的 DAO 实现将在 WHERE 子句中使用哪些列,以及这些列的使用频率,以便各个项目可以自行决定索引:

Table 1. SQL 语句中的 Where 子句(不包括主键)及其大致使用频率。

默认表名

Where 条件

频率

BATCH_JOB_INSTANCE

JOB_NAME = ? and JOB_KEY = ?

每次启动 job

BATCH_JOB_EXECUTION

JOB_INSTANCE_ID = ?

每次重新启动 job

BATCH_STEP_EXECUTION

VERSION = ?

在提交间隔,又称分块(以及step开始和结束时)

BATCH_STEP_EXECUTION

STEP_NAME = ? and JOB_EXECUTION_ID = ?

在执行每个 step 之前