Spring 批处理作业更好性能的设计思路
Design ideas for Spring Batch Job better performnace
我已经编写了一个 spring 批处理作业并使用了 SimpleAsyncTaskExecutor
。我尝试使用 ThreadPoolExecutor
,但它的性能比 SimpleAsyncTaskExecutor
慢得多。
这些是工作的要点,
1.Processing部分工作是最耗时的部分。它基本上将大量 SQL SELECTs
发送到与 reader 和编写器不同的数据库 table。
Reader 和 Writer 不需要太多 time.Processor 非常复杂。
2.There 是一项功能要求,一旦处理器 returns 一条记录,就将处理器的输出写入数据库。这是需要的,因为
处理器为作家找到东西是罕见的,我们需要
结果马上就坚持了。简而言之,它是一项业务
要求 chunk size =1
我担心工作表现。如果我使处理器逻辑轻一点,性能会增加很多,所以我猜处理器是瓶颈。
我只是用SimpleAsyncTaskExecutor
来实现并行。作业应该 运行 在强大的多处理器系统上。
关于 Spring 批处理方面我可以做些什么来加快这项工作有什么想法吗?
Job 有这一步。
@Bean
public Step step1(StepBuilderFactory stepBuilderFactory,
ItemReader<RemittanceVO> syncReader, ItemWriter<RemittanceClaimVO> writer,
ItemProcessor<RemittanceVO, RemittanceClaimVO> processor) {
return stepBuilderFactory.get("step1")
.listener(zeroReadRowsStepExecutionListener)
.<RemittanceVO, RemittanceClaimVO> chunk(Constants.SPRING_BATCH_CHUNK_SIZE)
.reader(syncReader)
.listener(afterReadListener)
.processor(processor)
.writer(writer)
.taskExecutor(simpleAsyntaskExecutor)
.throttleLimit(Constants.THROTTLE_LIMIT)
.build();
}
如果需要chunksize为1,那就没办法快了。您产生了太多的开销(例如更新每个项目的批处理表)。此外,从处理器调用数据库也会对性能产生非常负面的影响,因为您再次为每个项目生成 sql 调用。
在使用 DB 时,获得良好性能的关键是减少发送到 DB 的调用次数。例如。通过使用 batchUpdates 或 SQL 语句 select 整个块的数据,而不是单个项目的数据(使用适当的 IN 子句)。
当我必须读取额外的数据来处理我的项目时,我使用两种模式:
- 首先,我尝试 select 使用合并 reader 的附加数据(合并 reader 通过对数据进行排序将来自不同 reader 的数据合并在一起基于相同的键)
- 其次,如果那不可能,我使用一个特殊的编写器,它也执行处理部分。这样,您可以使用适当的 IN 子句实现 Select,该子句 select 是完整块的数据。所以如果是oracle的话,一千条数据只需要一次调用就可以了。
我已经编写了一个 spring 批处理作业并使用了 SimpleAsyncTaskExecutor
。我尝试使用 ThreadPoolExecutor
,但它的性能比 SimpleAsyncTaskExecutor
慢得多。
这些是工作的要点,
1.Processing部分工作是最耗时的部分。它基本上将大量 SQL SELECTs
发送到与 reader 和编写器不同的数据库 table。
Reader 和 Writer 不需要太多 time.Processor 非常复杂。
2.There 是一项功能要求,一旦处理器 returns 一条记录,就将处理器的输出写入数据库。这是需要的,因为
处理器为作家找到东西是罕见的,我们需要
结果马上就坚持了。简而言之,它是一项业务
要求 chunk size =1
我担心工作表现。如果我使处理器逻辑轻一点,性能会增加很多,所以我猜处理器是瓶颈。
我只是用SimpleAsyncTaskExecutor
来实现并行。作业应该 运行 在强大的多处理器系统上。
关于 Spring 批处理方面我可以做些什么来加快这项工作有什么想法吗?
Job 有这一步。
@Bean
public Step step1(StepBuilderFactory stepBuilderFactory,
ItemReader<RemittanceVO> syncReader, ItemWriter<RemittanceClaimVO> writer,
ItemProcessor<RemittanceVO, RemittanceClaimVO> processor) {
return stepBuilderFactory.get("step1")
.listener(zeroReadRowsStepExecutionListener)
.<RemittanceVO, RemittanceClaimVO> chunk(Constants.SPRING_BATCH_CHUNK_SIZE)
.reader(syncReader)
.listener(afterReadListener)
.processor(processor)
.writer(writer)
.taskExecutor(simpleAsyntaskExecutor)
.throttleLimit(Constants.THROTTLE_LIMIT)
.build();
}
如果需要chunksize为1,那就没办法快了。您产生了太多的开销(例如更新每个项目的批处理表)。此外,从处理器调用数据库也会对性能产生非常负面的影响,因为您再次为每个项目生成 sql 调用。
在使用 DB 时,获得良好性能的关键是减少发送到 DB 的调用次数。例如。通过使用 batchUpdates 或 SQL 语句 select 整个块的数据,而不是单个项目的数据(使用适当的 IN 子句)。
当我必须读取额外的数据来处理我的项目时,我使用两种模式:
- 首先,我尝试 select 使用合并 reader 的附加数据(合并 reader 通过对数据进行排序将来自不同 reader 的数据合并在一起基于相同的键)
- 其次,如果那不可能,我使用一个特殊的编写器,它也执行处理部分。这样,您可以使用适当的 IN 子句实现 Select,该子句 select 是完整块的数据。所以如果是oracle的话,一千条数据只需要一次调用就可以了。