SpringBatch概述
Spring Batch
是一个轻量级的、完善的批处理框架,旨在帮助企业建立健壮、高效的批处理应用。Spring Batch
是Spring
的一个子项目,使用Java
语言并基于Spring
框架为基础开发,使得已经使用Spring
框架的开发者或者企业更容易访问和利用企业服务。
Spring Batch
提供了大量可重用的组件,包括日志、追踪、事物、任务作业统计、任务重启、跳过、重复、资源管理。对于大数据量和高性能的批处理任务,Spring Batc
h 同样提供了高级功能和特性来支持,比如分区功能、远程功能。总之,通过Spring Batch
能够支持简单的、复杂的和大数据量的批处理作业。
Spring Batch
是一个批处理应用框架,不是调度框架,但需要和调度框架合作来构建完成批处理任务。它只关注批处理任务相关的问题,如事物、并发、监控、执行等,并不提供相应的调度功能。如果需要使用调度框架,在商业软件和开源软件中已经有很多优秀的企业级调度框架(如;Quartz
、Tivoli
、Control-M
、Cron
等)可以使用。
使用场景
一般的典型批处理程序:
从数据库,文件或队列中读取大量记录。
以某种方式处理数据。
以修改的形式写回数据。
Spring Batch自动执行此基本批处理迭代,提供处理类似事务的功能,通常在脱机环境中处理,无需任何用户交互。批处理作业是大多数IT项目的一部分,Spring Batch是唯一提供强大的企业级解决方案的开源框架。
业务场景
定期提交批处理
并发批处理:并行处理作业
分阶段的企业消息驱动处理
大规模并行批处理
失败后手动或预定重启
依赖步骤的顺序处理(使用扩展的toworkflow驱动批次)
部分处理:跳过记录(例如,回滚时)
整批交易,适用于批量较小或现有存储过程/脚本的情况
技术目标
批处理开发人员使用Spring编程模型:专注于业务逻辑,让框架负责基础架构。
清楚地分离基础架构,批处理执行环境和批处理应用程序之间的关注点。
提供通用的核心执行服务作为所有项目可以实现的接口。
提供可以“开箱即用”使用的核心执行接口的简单和默认实现。
通过在所有层中利用spring框架,易于配置,定制和扩展服务。
所有现有核心服务都应易于更换或扩展,而不会对基础架构层产生任何影响。
提供一个简单的部署模型,使用Maven构建的架构JAR与应用程序完全分离。
Spring Batch 体系结构
Spring Batch的设计具有可扩展性和多样化的最终用户群。下图显示了支持最终用户开发人员的可扩展性和易用性的分层体系结构。
这种分层架构突出了三个主要的高级组件:应用程序,核心和基础架构。该应用程序包含开发人员使用Spring Batch
编写的所有批处理作业和自定义代码。Batch Core
包含启动和控制批处理作业所需的核心运行时类。它包括实现 JobLauncher
,Job
和Step
。Application
和Core
都建立在通用基础架构之上。此基础结构包含常见的读取器和编写器和服务(例如RetryTemplate
),应用程序开发人员(读取器和编写器,如ItemReader
和ItemWriter
)以及核心框架本身(Retry
,它是自己的库)都使用它们。
一般Batch原则和指南
在构建批处理解决方案时,应考虑以下关键原则,指南和一般注意事项。
请记住,批处理体系结构通常会影响在线体系结构,反之亦然。尽可能使用通用构建块来设计体系结构和环境.
尽可能简化并避免在单批应用程序中构建复杂的逻辑结构。
保持数据的处理和存储物理上紧密相连(换句话说,将数据保存在处理过程中)。
最大限度地减少系统资源的使用,尤其是I/O. 在内部存储器中执行尽可能多的操作。
查看应用程序I/O(分析SQL语句)以确保避免不必要的物理I/O. 特别是,需要寻找以下四个常见缺陷:
① 当数据可以被读取一次并缓存或保存在工作存储中时,读取每个事务的数据。
②重新读取先前在同一事务中读取数据的事务的数据
③导致不必要的表或索引扫描。
④未在SQL语句的WHERE子句中指定键值。不要在批处理中执行两次操作。例如,如果您需要数据汇总以用于报告目的,则应该(如果可能)在最初处理数据时递增存储的总计,因此您的报告应用程序不必重新处理相同的数据。
在批处理应用程序开始时分配足够的内存,以避免在此过程中进行耗时的重新分配。
总是假设数据完整性最差。插入适当的检查和记录验证以维护数据完整性。
尽可能实现内部验证的校验和。例如,平面文件应该有一个预告片记录,告诉文件中的记录总数和关键字段的汇总。
在具有真实数据量的类似生产的环境中尽可能早地计划和执行压力测试。
在大批量系统中,备份可能具有挑战性,特别是如果系统以24*7为基础同时在线运行。数据库备份通常在在线设计中得到很好的处理,但文件备份应该被视为同样重要。如果系统依赖于平面文件,则文件备份过程不仅应该到位并记录在案,而且应该定期进行测试。
批处理策略
为了帮助设计和实现批处理系统,应该以样本结构图和代码shell的形式向设计人员和程序员提供基本的批处理应用程序构建块和模式。在开始设计批处理作业时,应将业务逻辑分解为一系列步骤,这些步骤可使用以下标准构建块实现:
转换应用程序:对于由外部系统提供或生成的每种类型的文件,必须创建转换应用程序以将提供的事务记录转换为处理所需的标准格式。此类批处理应用程序可以部分或全部由转换实用程序模块组成(请参阅基本批处理服务)。
验证应用程序:验证应用程序确保所有输入/输出记录正确且一致。验证通常基于文件头和预告片,校验和和验证算法以及记录级交叉检查。
验证应用程序:验证应用程序确保所有输入/输出记录正确且一致。验证通常基于文件头和预告片,校验和和验证算法以及记录级交叉检查。
提取应用程序:从数据库或输入文件中读取一组记录,根据预定义规则选择记录,并将记录写入输出文件的应用程序。
提取/更新应用程序:从数据库或输入文件中读取记录并对由每个输入记录中的数据驱动的数据库或输出文件进行更改的应用程序。
处理和更新应用程序:从提取或验证应用程序对输入事务执行处理的应用程序。处理通常涉及读取数据库以获取处理所需的数据,可能更新数据库并创建输出处理的记录。
处理和更新应用程序:从提取或验证应用程序对输入事务执行处理的应用程序。处理通常涉及读取数据库以获取处理所需的数据,可能更新数据库并创建输出处理的记录。
此外,应为无法使用前面提到的构建块构建的业务逻辑提供基本应用程序shell。
除了主要构建块之外,每个应用程序还可以使用一个或多个标准实用程序步骤,例如:
排序:读取输入文件并生成输出文件的程序,其中记录已根据记录中的排序键字段重新排序。排序通常由标准系统实用程序执行。
拆分:一个程序,它读取单个输入文件,并根据字段值将每个记录写入多个输出文件之一。可以通过参数驱动的标准系统实用程序来定制或执行拆分。
合并:一种程序,它从多个输入文件中读取记录,并生成一个输出文件,其中包含来自输入文件的组合数据。可以通过参数驱动的标准系统实用程序来定制或执行合并。
批量应用程序还可以按其输入源进行分类:
数据库驱动的应用程序由从数据库检索的行或值驱动。
文件驱动的应用程序由从文件中检索的记录或值驱动。
消息驱动的应用程序由从消息队列中检索的消息驱动。
任何批处理系统的基础都是处理策略。影响策略选择的因素包括:估计的批处理系统容量,与在线系统或其他批处理系统的并发性,可用的批处理窗口。(请注意,随着越来越多的企业希望全天候运行,明确的批处理窗口正在消失)。
批处理的典型处理选项是(按实现复杂度的递增顺序):
在离线模式下批处理窗口期间的正常处理。
并发批处理或在线处理。
并行处理许多不同的批处理运行或作业。
分区(同时处理同一作业的许多实例)。
上述选项的组合。
商业调度程序可以支持部分或全部这些选项。
以下部分更详细地讨论了这些处理选项。重要的是要注意,根据经验,批处理采用的提交和锁定策略取决于所执行的处理类型,并且在线锁定策略也应使用相同的原则。因此,在设计整体架构时,批处理架构不能简单地成为事后的想法。
锁定策略可以是仅使用普通数据库锁或在体系结构中实现其他自定义锁定服务。锁定服务将跟踪数据库锁定(例如,通过将必要信息存储在专用db表中)并对请求db操作的应用程序提供或拒绝权限。此架构还可以实现重试逻辑,以避免在发生锁定情况时中止批处理作业。
1.批处理窗口中的正常处理对于在单独的批处理窗口中运行的简单批处理进程,其中在线用户或其他批处理过程不需要更新的数据,并发性不是问题,可以在批次运行结束。
在大多数情况下,更强大的方法更合适。请记住,批量系统随着时间的推移有增长的趋势,无论是复杂性还是处理的数据量。如果没有锁定策略并且系统仍然依赖于单个提交点,则修改批处理程序可能会很痛苦。因此,即使使用最简单的批处理系统,也需要考虑重启恢复选项的提交逻辑以及本节后面描述的更复杂情况的信息。
2.并发批处理或在线处理批处理应用程序处理可由在线用户同时更新的数据不应锁定在线用户可能需要的任何数据(数据库或文件中)几秒钟。此外,应在每次交易结束时将更新提交到数据库。这最小化了其他进程不可用的数据部分以及数据不可用的时间。
最小化物理锁定的另一个选项是使用乐观锁定模式或悲观锁定模式实现逻辑行级锁定。
乐观锁定假设记录争用的可能性很小。它通常意味着在批处理和联机处理同时使用的每个数据库表中插入时间戳列。当应用程序提取行进行处理时,它还会获取时间戳。当应用程序尝试更新已处理的行时,更新将使用WHERE子句中的原始时间戳。如果时间戳匹配,则更新数据和时间戳。如果时间戳不匹配,则表示另一个应用程序在获取和更新尝试之间更新了同一行。因此,无法执行更新。
悲观锁定是任何锁定策略,假设存在记录争用的高可能性,因此需要在检索时获得物理或逻辑锁定。一种悲观逻辑锁定使用数据库表中的专用锁定列。当应用程序检索要更新的行时,它会在锁定列中设置一个标志。使用该标志后,尝试检索同一行的其他应用程序在逻辑上会失败。当设置标志的应用程序更新该行时,它还会清除该标志,从而使该行能够被其他应用程序检索。请注意,必须在初始提取和标志设置之间保持数据的完整性,例如使用数据库锁(例如SELECT FOR UPDATE)。另请注意,此方法的缺点与物理锁定相同,除了管理构建超时机制更容易,如果用户在锁定记录时进入午餐,则会释放锁定。
这些模式不一定适合批处理,但它们可能用于并发批处理和联机处理(例如在数据库不支持行级锁定的情况下)。作为一般规则,乐观锁定更适合在线应用程序,而悲观锁定更适合批处理应用程序。每当使用逻辑锁定时,必须对访问受逻辑锁保护的数据实体的所有应用程序使用相同的方案。
请注意,这两种解决方案仅解决锁定单个记录的问题。通常,我们可能需要锁定逻辑上相关的记录组。使用物理锁,您必须非常小心地管理这些,以避免潜在的死锁。对于逻辑锁,通常最好构建一个逻辑锁管理器,该管理器了解您要保护的逻辑记录组,并确保锁是连贯的和非死锁的。此逻辑锁管理器通常使用自己的表进行锁管理,争用报告,超时机制和其他问题。
3.并行处理并行处理允许多个批处理运行或作业并行运行,以最大限度地减少总批处理时间。只要作业不共享相同的文件,db-tables或索引空间,这就不是问题。如果他们这样做,则应使用分区数据实现此服务。另一种选择是通过使用控制表来构建用于维护相互依赖性的体系结构模块。控制表应包含每个共享资源的行以及它是否正由应用程序使用。然后,批处理体系结构或并行作业中的应用程序将从该表中检索信息,以确定它是否可以访问它所需的资源。
如果数据访问不是问题,则可以通过使用额外的线程并行处理来实现并行处理。在大型机环境中,传统上使用并行作业类,以确保所有进程有足够的CPU时间。无论如何,该解决方案必须足够强大,以确保所有正在运行的进程的时间片。
并行处理中的其他关键问题包括负载平衡和一般系统资源(如文件,数据库缓冲池等)的可用性。另请注意,控制表本身很容易成为关键资源。
4.分区使用分区允许多个版本的大批量应用程序同时运行。这样做的目的是减少处理长批处理作业所需的时间。可以成功分区的进程是可以拆分输入文件和/或对主数据库表进行分区以允许应用程序针对不同数据集运行的进程。
此外,必须将分区的进程设计为仅处理其分配的数据集。分区架构必须与数据库设计和数据库分区策略紧密相关。请注意,数据库分区并不一定意味着数据库的物理分区,尽管在大多数情况下这是可取的。下图说明了分区方法:
该体系结构应足够灵活,以允许动态配置分区数。应考虑自动和用户控制的配置。自动配置可以基于诸如输入文件大小和输入记录的数量之类的参数。
4.1分区方法选择分区方法必须根据具体情况进行。以下列表描述了一些可能的分区方法:
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处理的行。
然后,用于检索记录的过程将有一个附加WHERE子句来选择由特定指示符标记的所有行。此表中的插入将涉及添加标记字段,该字段将默认为其中一个实例(例如“A”)。
将使用简单的批处理应用程序来更新指示符,例如在不同实例之间重新分配负载。当添加了足够多的新行时,可以运行此批处理(除批处理窗口外的任何时间)以将新行重新分发到其他实例。
批处理应用程序的其他实例仅需要运行前面段落中所述的批处理应用程序,以重新分配指示符以使用新的实例数。
4.2数据库和应用程序设计原则
支持使用键列方法针对分区数据库表运行的多分区应用程序的体系结构应包括用于存储分区参数的中央分区存储库。这提供了灵活性并确保了可维护性。存储库通常由单个表组成,称为分区表。
存储在分区表中的信息是静态的,通常应由DBA维护。该表应包含多行分区应用程序的每个分区的一行信息。该表应包含程序ID代码,分区号(分区的逻辑ID),此分区的db键列的低值以及此分区的db键列的高值。
在程序启动时,id应该从体系结构(特别是从Control Processing Tasklet)将程序和分区号传递给应用程序。如果使用键列方法,则使用这些变量来读取分区表,以确定应用程序要处理的数据范围。此外,在整个处理过程中必须使用分区号:
添加到输出文件/数据库更新,以使合并过程正常工作。
向批处理日志报告正常处理,并向架构错误处理程序报告任何错误。
4.3尽量减少死锁
当应用程序并行运行或分区时,可能会发生数据库资源争用和死锁。作为数据库设计的一部分,数据库设计团队尽可能地消除潜在的争用情况至关重要。
此外,开发人员必须确保数据库索引表的设计时考虑到死锁和性能。
管理或体系结构表中经常出现死锁或热点,例如日志表,控制表和锁定表。还应考虑到这些问题的影响。实际压力测试对于识别架构中可能存在的瓶颈至关重要。
为了最大限度地减少冲突对数据的影响,架构应在附加到数据库或遇到死锁时提供等待和重试间隔等服务。这意味着内置机制对某些数据库返回代码作出反应,而不是发出立即错误,等待预定的时间并重试数据库操作。
4.4参数传递和验证
分区架构应该对应用程序开发人员相对透明。该体系结构应执行与以分区模式运行应用程序相关的所有任务,包括:
在应用程序启动之前检索分区参数。
在应用程序启动之前验证分区参数。
在启动时将参数传递给应用程序。
验证应包括检查以确保:
该应用程序有足够的分区来覆盖整个数据范围。
分区之间没有间隙。
如果数据库已分区,则可能需要进行一些额外的验证,以确保单个分区不跨越数据库分区。
此外,该体系结构应考虑分区的合并。关键问题包括:
在进入下一个工作步骤之前,是否必须完成所有分区?
如果其中一个分区中止会发生什么?