Spring Batch:用于高容量和低延迟的 ItemReader 实现

Posted

技术标签:

【中文标题】Spring Batch:用于高容量和低延迟的 ItemReader 实现【英文标题】:Spring Batch: Which ItemReader implementation to use for high volume & low latency 【发布时间】:2020-12-23 07:32:11 【问题描述】:

用例:从数据库中读取 1000 万行 [10 列] 并写入文件(csv 格式)。

    建议 JdbcCursorItemReaderJdbcPagingItemReader 中的哪个 ItemReader 实现?原因是什么?

    在上述用例中哪个性能更好(更快)?

    单进程与多进程方法的选择会有所不同吗?

    如果是使用 TaskExecutor 的多线程方法,哪一个更好更简单?

【问题讨论】:

分区以创建部分 csv + 额外步骤将 csv 部分连接成单个 csv? @bellabax [暂时] 请考虑单进程方法。更新了询问单进程/多进程是否重要的​​问题。 【参考方案1】:

要处理这种数据,如果可能的话,您可能会想要并行化它(唯一阻止它的是输出文件需要保留来自输入的顺序)。假设您要并行处理您的处理,那么对于这种类型的用例,您有两个主要选项(根据您提供的内容):

    多线程步骤 - 这将处理每个线程的块,直到完成。这允许以非常简单的方式进行并行化(只需将 TaskExecutor 添加到您的步骤定义中)。有了这个,您就可以轻松地重新启动,因为您需要关闭您提到的任何一个 ItemReader 上的状态持久性(有一些方法可以解决这个问题,将数据库中的记录标记为已处理等)。李> 分区 - 这会将您的输入数据分解为由步骤实例并行处理的分区(主/从配置)。分区可以通过线程(通过 TaskExecutor)在本地执行,也可以通过远程分区远程执行。在任何一种情况下,您都可以通过并行化获得可重新启动性(每个步骤都处理它自己的数据,因此没有从分区到分区的状态踩踏)。

我做了一个关于与 Spring Batch 并行处理数据的演讲。具体来说,我展示的示例是一个远程分区作业。你可以在这里查看:https://www.youtube.com/watch?v=CYTj5YT7CZU

针对您的具体问题:

    建议在 JdbcCursorItemReader 和 JdbcPagingItemReader 中使用哪个 ItemReader 实现?原因是什么? - 这两个选项中的任何一个都可以调整以满足许多性能需求。这实际上取决于您使用的数据库、可用的驱动程序选项以及您可以支持的处理模型。另一个考虑因素是,您需要可重启性吗? 在上述用例中哪个性能更好(更快)? - 同样取决于您选择的处理模型。 在单进程与多进程方法的情况下,选择会有所不同吗? - 这取决于您如何管理作业,而不是 Spring Batch 可以处理的。问题是,您是要管理作业外部的分区(将数据描述作为参数传递给作业)还是要作业管理它(通过分区)。 在使用 TaskExecutor 的多线程方法的情况下,哪个更好更简单? - 我不会否认远程分区增加了本地分区和多线程步骤所没有的复杂程度.

我将从基本的步骤定义开始。然后尝试多线程步骤。如果这不能满足您的需求,则转到本地分区,最后在需要时进行远程分区。请记住,Spring Batch 旨在使该进程尽可能轻松。您可以从常规步骤转到仅配置更新的多线程步骤。要进行分区,您需要添加一个新类(一个 Partitioner 实现)和一些配置更新。

最后一点。其中大部分都谈到了并行处理这些数据。 Spring Batch 的 FlatFileItemWriter 不是线程安全的。最好的办法是并行写入多个文件,然后如果速度是您的首要关注点,然后将它们聚合起来。

【讨论】:

您在分区之前确实提到了Multithreaded step。但是,在多线程步骤中出现 JdbcCursorItemReader 的情况下,可能会出现 ResultSetExhaustedException。怎么解决? JdbcCursorItemReader 不被认为是线程安全的。在多线程步骤中使用 JdbcPagingItemReader。您可以在此处阅读有关原因的详细信息:jira.springsource.org/browse/…【参考方案2】:

您应该对此进行分析以便做出选择。在普通的 JDBC 中,我将从以下内容开始:

准备带有ResultSet.TYPE_FORWARD_ONLYResultSet.CONCUR_READ_ONLY 的语句。几个 JDBC 驱动程序在客户端“模拟”游标,除非您使用这两个,并且对于大型结果集,您不希望这样做,因为它可能会引导您到 OutOfMemoryError,因为您的 JDBC 驱动程序正在缓冲内存中的整个数据集。通过使用这些选项,您可以增加获得服务器端游标并将结果“流式传输”给您的机会,这是您想要的大型结果集。请注意,某些 JDBC 驱动程序总是在客户端“模拟”游标,因此本技巧可能对您的特定 DBMS 无用。 设置一个合理的fetch size,将网络往返的影响降到最低。 50-100 通常是分析的良好起始值。由于提取大小是提示,这对于您的特定 DBMS 也可能无用。

JdbcCursorItemReader 似乎涵盖了这两件事,但正如之前所说,它们不能保证在所有 DBMS 中都能为您提供最佳性能,所以我会从这个开始,然后,如果性能不足,请尝试 JdbcPagingItemReader

我不认为使用JdbcCursorItemReader 进行简单处理对于您的数据集大小来说会很慢,除非您有非常严格的性能要求。如果您真的需要使用JdbcPagingItemReader进行并行化可能会更容易,但是这两者的界面非常相似,所以我不会指望它。

无论如何,个人资料

【讨论】:

以上是关于Spring Batch:用于高容量和低延迟的 ItemReader 实现的主要内容,如果未能解决你的问题,请参考以下文章

最常见的15个Java多线程,并发面试问题

spring batch批量处理框架

spring-batch (ItemProcessor) 数据处理过程

禁用 Spring Batch 作业

什么是STM32的高寄存器和低寄存器?

Maven 打包“JAR”不适用于 spring-boot-starter-batch