Spring Batch 的设计考虑了可扩展性和不同的最终用户群体。
这种分层体系结构突出了三个主要的高级组件:应用程序、 核心和基础结构。应用程序包含所有批处理作业和编写的自定义代码 由开发人员使用 Spring Batch。批处理核心包含核心运行时类 启动和控制批处理作业所必需的。它包括 、 和 的实现。应用程序和核心都构建在一个共同的之上 基础设施。此基础结构包含通用读取器和写入器以及服务 (例如 ),应用程序开发人员(读者和 编写器,例如 和 ),以及核心框架本身(重试、 这是它自己的库)。JobLauncherJobStepRetryTemplateItemReaderItemWriter
一般批次原则和准则
应考虑以下关键原则、准则和一般注意事项 生成批处理解决方案时。
- 请记住,批处理架构通常会影响在线架构和副架构 反之亦然。使用通用建筑进行设计时同时考虑体系结构和环境 尽可能阻止。
- 尽可能简化,避免在单个逻辑结构中构建复杂的逻辑结构 批量应用程序。
- 保持数据的处理和存储物理上紧密地结合在一起(换句话说,保持 您进行处理的数据)。
- 最大限度地减少系统资源使用,尤其是 I/O。在 中执行尽可能多的操作 内部存储器。
- 查看应用程序 I/O(分析 SQL 语句)以确保不必要的物理 I/O 被避免。特别是,需要寻找以下四个常见缺陷:
- 读取每个事务的数据,当数据可以读取一次并缓存或保留时 在工作存储中。
- 重新读取之前在同一事务中读取数据的事务的数据 交易。
- 导致不必要的表或索引扫描。
- 未在 SQL 语句的子句中指定键值。WHERE
- 不要在批处理运行中执行两次操作。例如,如果您需要数据汇总 出于报告目的,您应该(如果可能)在数据存在时增加存储的总计 最初已处理,因此您的报告应用程序不必重新处理相同的内容 数据。
- 在批处理应用程序开始时分配足够的内存以避免耗时 在此过程中重新分配。
- 始终假设数据完整性方面的最坏情况。插入足够的检查和 记录验证以保持数据完整性。
- 尽可能为内部验证实施校验和。例如,平面文件 应该有一个预告片记录,告诉文件中的记录总数和 关键字段。
- 在类似生产的环境中尽早计划和执行压力测试 具有真实的数据量。
- 在大型批处理系统中,备份可能具有挑战性,尤其是在系统正在运行的情况下 24-7 全天候在线申请。数据库备份通常得到很好的照顾 在在线设计中,但文件备份应该被认为同样重要。 如果系统依赖于平面文件,则文件备份过程不仅应到位 并记录在案,但也要定期进行测试。
批处理策略
帮助设计和实现批处理系统、基本批处理应用程序构建块和 模式应以示例的形式提供给设计人员和程序员 结构图和代码外壳。开始设计批处理作业时,业务逻辑 应分解为一系列步骤,可以使用以下内容实现 标准构建块:
- 转换应用:对于由 提供或生成的每种类型的文件 外部系统,必须创建转换应用程序才能转换事务 以处理所需的标准格式提供的记录。这种类型的批次 应用程序可以部分或全部由翻译实用程序模块组成(请参阅基本 批处理服务)。
- 验证应用:验证应用程序可确保所有输入和输出 记录是正确和一致的。验证通常基于文件头和 预告片、校验和和验证算法,以及记录级交叉检查。
- 提取应用:数据提取应用程序从数据库中读取一组记录或 输入文件,根据预定义的规则选择记录,并将记录写入 输出文件。
- 提取/更新应用程序:提取/更新应用程序从数据库或 输入文件,并根据找到的数据对数据库或输出文件进行更改 在每个输入记录中。
- 处理和更新应用程序:处理和更新应用程序在 来自数据提取或验证应用程序的输入事务。处理通常 涉及读取数据库以获取处理所需的数据,可能更新 数据库并创建用于输出处理的记录。
- 输出/格式化应用:输出/格式化应用程序读取输入文件,重构数据 根据标准格式从此记录中生成输出文件以进行打印 或传输到另一个程序或系统。
此外,应为不能 使用前面提到的构建基块进行构建。
除了主要构建块之外,每个应用程序还可以使用一个或多个标准 实用程序步骤,例如:
- 排序:读取输入文件并生成输出文件的程序,其中记录 已根据记录中的排序键字段重新排序。排序通常是 由标准系统实用程序执行。
- 拆分:读取单个输入文件并将每条记录写入其中一条记录的程序 基于字段值的多个输出文件。拆分可以定制或执行 参数驱动的标准系统实用程序。
- 合并:从多个输入文件中读取记录并生成一个输出的程序 文件包含输入文件中的合并数据。合并可以由 参数驱动的标准系统实用程序。
批处理应用程序还可以按其输入源进行分类:
- 数据库驱动的应用程序由从数据库中检索的行或值驱动。
- 文件驱动的应用程序由从文件中检索的记录或值驱动。
- 消息驱动的应用程序由从消息队列检索的消息驱动。
任何批处理系统的基础都是处理策略。影响 策略的选择包括:估计的批处理系统量,并发性 联机系统或与其他批处理系统一起使用,可用的批处理窗口。(请注意,与 更多希望启动并运行 24x7 全天候、清晰的批处理窗口的企业是 消失)。
批处理的典型处理选项是(按实现的递增顺序排列) 复杂性):
- 脱机模式下批处理窗口期间的正常处理。
- 并发批处理或联机处理。
- 同时并行处理许多不同的批处理运行或作业。
- 分区(同时处理同一作业的多个实例)。
- 上述选项的组合。
商业调度程序可能支持其中一些或全部选项。
本节的其余部分将更详细地讨论这些处理选项。 请注意,根据经验,批处理采用的提交和锁定策略 过程取决于执行的处理类型以及在线锁定 战略也应该使用相同的原则。因此,批处理体系结构不能 在设计整体架构时,这只是事后的想法。
锁定策略可以是仅使用普通数据库锁或实现 体系结构中的其他自定义锁定服务。锁定服务将跟踪 数据库锁定(例如,通过将必要的信息存储在专用 数据库表),并向请求数据库的应用程序授予或拒绝权限 操作。重试逻辑也可以由此体系结构实现,以避免中止 锁定情况下的批处理作业。
1. 批处理窗口中的正常处理 用于在单独运行中的简单批处理 联机用户或其他批处理不需要要更新数据的批处理窗口 进程,并发性不是问题,可以在 批量运行。
在大多数情况下,更可靠的方法更合适。请记住,批次 随着时间的推移,系统在复杂性和数据方面都有增长的趋势 他们处理的卷。如果没有锁定策略,并且系统仍然依赖于 单提交点,修改批处理程序可能会很痛苦。因此,即使 最简单的批处理系统,考虑重启-恢复需要提交逻辑 选项以及有关稍后描述的更复杂情况的信息 本节。
2. 并发批处理或在线处理 批处理应用程序处理的数据 由在线用户同时更新不应锁定任何数据(无论是在 数据库或文件中),在线用户可能需要多个 秒。此外,更新应在每隔几个结束时提交到数据库 交易。这样做可以最大程度地减少其他进程不可用的数据部分 以及数据不可用的经过时间。
最小化物理锁定的另一个选项是使用逻辑行级锁定 使用乐观锁定模式或悲观锁定模式实现。
- 乐观锁定假定记录争用的可能性较低。它通常意味着 在每个数据库表中插入一个时间戳列,该列由批处理和 在线处理。当应用程序获取行进行处理时,它还读取 时间戳。当应用程序随后尝试更新已处理的行时,更新将使用 子句中的原始时间戳。如果时间戳匹配,则数据和 时间戳已更新。如果时间戳不匹配,则表示另一个 应用程序已更新提取和更新尝试之间的同一行。因此 无法执行更新。WHERE
- 悲观锁定是任何假设很有可能 记录争用,因此,需要在 检索时间。一种悲观逻辑锁定在 数据库表。当应用程序检索要更新的行时,它会在 锁定列。在标志就位的情况下,其他应用程序尝试检索 同一行在逻辑上失败。当设置标志的应用程序更新行时,它还 清除该标志,使该行能够由其他应用程序检索。请注意, 在初始获取和设置之间也必须保持数据的完整性 — 例如,通过使用数据库锁(如 )。另请注意 这种方法与物理锁定具有相同的缺点,只是它有点 更易于管理 构建超时机制,如果用户释放锁,则解锁 在记录锁定时去吃午饭。SELECT FOR UPDATE
这些模式不一定适合批处理,但可以使用 用于并发批处理和联机处理(例如,在数据库不 支持行级锁定)。作为一般规则,乐观锁定更适合 在线应用,而悲观锁定更适合批量申请。 无论何时使用逻辑锁定,都必须对所有应用程序使用相同的方案 访问受逻辑锁保护的数据实体。
请注意,这两种解决方案都只解决锁定单个记录的问题。通常,我们可能会 需要锁定逻辑相关的记录组。使用物理锁,您必须 非常仔细地管理这些以避免潜在的死锁。使用逻辑锁,它 通常最好构建一个理解逻辑记录的逻辑锁管理器 要保护的组,可以确保锁是一致的,并且 非死锁。此逻辑锁管理器通常使用自己的表进行锁定 管理、争用报告、超时机制和其他问题。
3. 并行处理 并行处理允许多个批处理运行或作业运行 并行以最大程度地减少总经过的批处理时间。这不是问题,因为 只要作业不共享相同的文件、数据库表或索引空间。如果他们这样做, 此服务应使用分区数据实现。另一种选择是构建一个 体系结构模块,用于通过使用控制表来维护相互依赖关系。一个控件 表应包含每个共享资源的一行,以及该共享资源是否正由 申请与否。批处理体系结构或并行作业中的应用程序将 然后从该表中检索信息,以确定它是否可以访问 它需要的资源。
如果数据访问不是问题,则可以通过 使用其他线程并行处理。在大型机环境中,并行 传统上使用作业类,以确保所有人都有足够的 CPU 时间 过程。无论如何,解决方案必须足够强大,以确保时间片 所有正在运行的进程。
并行处理中的其他关键问题包括负载平衡和可用性 常规系统资源,如文件、数据库缓冲池等。另外,请注意 控制表本身很容易成为关键资源。
4. 分区 使用分区允许多个版本的大批量应用程序 并发运行。这样做的目的是减少 处理长批处理作业。可以成功分区的进程是那些 可以拆分输入文件或对主数据库表进行分区,以便 应用程序针对不同的数据集运行。
此外,分区的进程必须设计为仅处理其 分配的数据集。分区体系结构必须与数据库紧密绑定 设计和数据库分区策略。请注意,数据库分区不会 必然意味着数据库的物理分区(尽管在大多数情况下,这是 建议)。
架构应该足够灵活,以允许动态配置数字 的分区。您应该同时考虑自动配置和用户控制配置。 自动配置可以基于输入文件大小和 输入记录数。
4.1 分区方法选择分区方法必须在 视具体情况而定。以下列表描述了一些可能的分区 方法:
1.记录集的固定甚至分解
这涉及将输入记录集分解为偶数个部分(例如, 10,其中每个部分恰好占整个记录集的 1/10)。然后每个部分 由批处理/数据提取应用程序的一个实例处理。
若要使用此方法,需要进行预处理以拆分记录设置。这 此拆分的结果是您可以使用的下限和上限放置编号 作为批处理/提取应用程序的输入,以将其处理限制为仅其 部分。
预处理可能是一个很大的开销,因为它必须计算和确定边界 记录集的每个部分。
2. 按键列分解
这涉及按键列(如位置代码)分解输入记录集, 并将每个键中的数据分配给批处理实例。为此,列 值可以是:
- 由分区表分配给批处理实例(稍后将在此中介绍) 部分)。
- 按值的一部分分配给批处理实例(例如 0000-0999、1000 - 1999、 等等)。
在选项 1 下,添加新值意味着手动将批处理或数据提取重新配置为 确保将新值添加到特定实例。
在选项 2 下,这可确保批处理的实例涵盖所有值 工作。但是,一个实例处理的值数取决于 列值的分布(在 0000-0999 中可能存在大量位置 范围,很少在 1000-1999 范围内)。在此选项下,数据范围应为 设计时考虑了分区。
在这两个选项下,记录到批处理实例的最佳均匀分布不能 实现。使用的批处理实例数没有动态配置。
3. 按观点分手
这种方法基本上是按键列分解的,但在数据库级别。它涉及 将记录集分解为视图。批处理的每个实例都使用这些视图 处理过程中的应用。分解是通过对数据进行分组来完成的。
使用此选项,批处理应用程序的每个实例都必须配置为命中 特定视图(而不是主表)。此外,随着新数据的添加 值,这组新数据必须包含在视图中。没有动态 配置功能,因为实例数的变化会导致 的景色。
4. 增加处理指标
这涉及向输入表添加一个新列,该列充当 指示器。作为预处理步骤,所有指标都标记为未处理。 在批处理应用程序的记录提取阶段,根据条件读取记录 单个记录被标记为未处理,并且一旦被读取(带锁定), 它被标记为正在处理中。当该记录完成时,指标是 更新为“完成”或“错误”。您可以启动批处理应用程序的多个实例 无需更改,因为附加列可确保记录仅处理一次。
使用此选项,表上的 I/O 将动态增加。在更新的情况下 批处理应用程序,则减少了这种影响,因为无论如何都必须进行写入。
5. 将表提取到平面文件
此方法涉及将表提取到平面文件中。然后可以将此文件拆分为 多个段,并用作批处理实例的输入。
使用此选项,将表提取到文件中的额外开销和 拆分它可能会抵消多分区的效果。动态配置可以 通过更改文件拆分脚本来实现。
6. 哈希列的使用
此方案涉及向数据库表添加哈希列(键或索引) 用于检索驱动程序记录。此哈希列具有一个指示符,用于确定哪个 批处理应用程序的实例处理此特定行。例如,如果有 是要启动的三个批处理实例,“A”的指示器标记了一行 按实例 1 处理,“B”的指示符标记实例 2 处理的行, “C”的指示器标记了实例 3 处理的行。
然后,用于检索记录的过程将具有附加子句 以选择由特定指示器标记的所有行。此表中的插入项将 涉及添加标记字段,该字段将默认为 实例(例如“A”)。WHERE
一个简单的批处理应用程序将用于更新指标,例如 在不同实例之间重新分配负载。当足够大的数字 已添加新行,可以运行此批处理(随时运行,批处理窗口中除外) 将新行重新分发到其他实例。
批处理应用程序的其他实例只需要运行批处理 应用(如前几段所述)将指标重新分配给 使用新数量的实例。
4.2 数据库与应用设计原则
支持针对以下情况运行的多分区应用程序的体系结构 分区数据库表和使用键列方法应包括一个中央 用于存储分区参数的分区存储库。这提供了灵活性和 确保可维护性。存储库通常由单个表组成,称为 分区表。
存储在分区表中的信息是静态的,通常应进行维护 由DBA提供。该表应包含 多分区应用程序。该表应具有程序 ID 代码列, 分区号(分区的逻辑 ID),此分区的数据库键列的低值 分区,以及此分区的数据库键列的高值。
在程序启动时,程序和分区号应传递给 来自体系结构的应用程序(特别是来自控制处理任务包的应用程序)。如果 使用键列方法,这些变量用于读取分区表 以确定应用程序要处理的数据范围。此外, 在整个处理过程中必须使用分区号,以便:id
- 添加到输出文件或数据库更新,以便合并过程正常工作 适当地。
- 向批处理日志报告正常处理,向体系结构错误报告任何错误 处理器。
4.3 最小化死锁
当应用程序并行运行或分区时,对数据库资源的争用 并且可能会出现死锁。数据库设计团队必须消除 尽可能多地将潜在的争用情况作为数据库设计的一部分。
此外,开发人员必须确保数据库索引表的设计与 牢记死锁预防和性能。
死锁或热点经常出现在管理表或体系结构表中,例如日志 表、控制表和锁定表。这些影响应纳入 帐户也是如此。实际的压力测试对于确定可能的 体系结构中的瓶颈。
为了最大程度地减少冲突对数据的影响,体系结构应提供服务 (如等待和重试间隔)在附加到数据库或遇到 僵局。这意味着一个内置的机制来响应某些数据库返回代码,并且, 而不是立即发出错误,等待预定的时间量和 重试数据库操作。
4.4 参数传递和验证
分区体系结构应该对应用程序开发人员相对透明。 体系结构应执行与在 分区模式,包括:
- 在应用程序启动之前检索分区参数。
- 在应用程序启动之前验证分区参数。
- 在启动时将参数传递给应用程序。
验证应包括检查,以确保:
- 应用程序有足够的分区来覆盖整个数据范围。
- 分区之间没有间隙。
如果数据库已分区,则可能需要一些额外的验证以确保 单个分区不跨越数据库分区。
此外,体系结构应考虑分区的合并。 关键问题包括:
- 在进入下一个作业步骤之前,是否必须完成所有分区?
- 如果其中一个分区中止,会发生什么情况?