使用spring批处理分区处理海量数据

使用spring批处理分区处理海量数据,spring,spring-batch,Spring,Spring Batch,我正在实现spring批处理作业,以便使用分区方法处理DB表中的数百万条记录,如下所示- 从分区器中的表中获取唯一的分区代码,并在执行上下文中设置相同的代码 使用读卡器、处理器和写入器创建一个区块步骤,根据特定的分区代码处理记录 这种方法是正确的还是有更好的方法来应对这种情况?由于某些分区代码的记录数可能比其他分区代码多,因此具有更多记录的分区代码可能比具有较少记录的分区代码需要更多的时间来处理 是否可以像thread1进程1-1000、thread2进程1001-2000等创建要处理的分区/线

我正在实现spring批处理作业,以便使用分区方法处理DB表中的数百万条记录,如下所示-

  • 从分区器中的表中获取唯一的分区代码,并在执行上下文中设置相同的代码

  • 使用读卡器、处理器和写入器创建一个区块步骤,根据特定的分区代码处理记录

  • 这种方法是正确的还是有更好的方法来应对这种情况?由于某些分区代码的记录数可能比其他分区代码多,因此具有更多记录的分区代码可能比具有较少记录的分区代码需要更多的时间来处理

    是否可以像thread1进程1-1000、thread2进程1001-2000等创建要处理的分区/线程

    我如何控制创建的线程数,因为分区代码可以是100个左右,我只想创建20个线程,并在5次迭代中处理

    如果一个分区出现故障,会发生什么情况?所有处理都会停止并恢复吗

    以下是配置-

     <bean id="MyPartitioner" class="com.MyPartitioner" />
     <bean id="itemProcessor" class="com.MyProcessor" scope="step" />
     <bean id="itemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader" scope="step" >
      <property name="dataSource" ref="dataSource"/>
      <property name="sql" value="select * from mytable WHERE code = '#{stepExecutionContext[code]}' "/>
      <property name="rowMapper">
          <bean class="com.MyRowMapper" scope="step"/>
      </property>
    </bean>
    <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >
        <property name="corePoolSize" value="20"/>
        <property name="maxPoolSize" value="20"/>
        <property name="allowCoreThreadTimeOut" value="true"/>
    </bean>
    
    <batch:step id="Step1" xmlns="http://www.springframework.org/schema/batch">
        <batch:tasklet transaction-manager="transactionManager">
            <batch:chunk reader="itemReader"  processor="itemProcessor" writer="itemWriter" commit-interval="200"/>
        </batch:tasklet>
    </batch:step>
    <batch:job id="myjob">
        <batch:step id="mystep">
            <batch:partition step="Step1" partitioner="MyPartitioner">
                <batch:handler grid-size="20" task-executor="taskExecutor"/>
            </batch:partition>
        </batch:step>
    </batch:job>
    
    
    
    分割者-

    public class MyPartitioner implements Partitioner{
    @Override
    public Map<String, ExecutionContext> partition(int gridSize)
    {
    Map<String, ExecutionContext> partitionMap = new HashMap<String, ExecutionContext>();
    List<String> codes = getCodes();
    
    for (String code : codes)
    {
        ExecutionContext context = new ExecutionContext();
        context.put("code", code);
        partitionMap.put(code, context);
    }
    return partitionMap;}}
    
    公共类MyPartitioner实现了分区器{
    @凌驾
    公共地图分区(int gridSize)
    {
    Map partitionMap=newhashmap();
    列表代码=getCodes();
    用于(字符串代码:代码)
    {
    ExecutionContext=新的ExecutionContext();
    上下文。放置(“代码”,代码);
    partitionMap.put(代码、上下文);
    }
    返回分区映射;}}
    

    谢谢

    我想说这是一种正确的方法,我不明白为什么每1000个项目需要一个线程,如果您按照唯一的分区代码进行分区,并且有1000个项目的区块,那么您将在每个线程上有1000个项目的事务,这是可以的

  • 除了保存唯一的分区代码外,还可以计算 每个分区的代码和分区都有很多,甚至更多 为每1000个相同分区代码创建新的子上下文(即 分区代码的方式,即2200条记录,您将调用3 具有上下文参数的线程:1=>partition_key=key1,skip=0, count=1000,2=>partition\u key=key1,skip=1000,count=1000和 3=>partition_key=key1,skip=2000,count=1000)如果您需要 我想要,但我还是不想要

  • 线程的数量由
    ThreadPoolTaskExecutor
    控制,当您创建分区步骤时,它被传递给分区步骤。您有方法
    setCorePoolSize()
    ,您可以将其设置为20,最多可以获得20个线程。下一个细粒度配置是
    网格大小
    ,它告诉您将在完整分区映射中创建多少个分区。这是。因此,划分就是划分工作。之后,线程配置将定义实际处理的并发性

  • 如果一个分区失败,整个分区步骤将失败,并显示哪个分区失败的信息。成功分区已完成,不会再次调用,当作业重新启动时,它将重新执行失败和未处理的分区,从而恢复停止的位置

  • 希望我能回答你所有的问题,因为问题很多

    案例1的示例-可能有错误,但只是想了解一下:

    public class MyPartitioner implements Partitioner{
    @Override
    public Map<String, ExecutionContext> partition(int gridSize)
    {
        Map<String, ExecutionContext> partitionMap = new HashMap<String, ExecutionContext>();
        Map<String, int> codesWithCounts = getCodesWithCounts();
    
        for (Entry<String, int> codeWithCount : codesWithCounts.entrySet())
        {
            for (int i = 0; i < codeWithCount.getValue(); i + 1000){
                ExecutionContext context = new ExecutionContext();
                context.put("code", code);
                context.put("skip", i);
                context.put("count", 1000);
                partitionMap.put(code, context);
            }
        }
        return partitionMap;
    }
    
    公共类MyPartitioner实现了分区器{
    @凌驾
    公共地图分区(int gridSize)
    {
    Map partitionMap=newhashmap();
    Map codesWithCounts=getCodesWithCounts();
    for(条目codeWithCount:codesWithCounts.entrySet())
    {
    对于(int i=0;i

    Adn比你翻页1000,你从上下文中得到你应该跳过多少,在2200的例子中是:0,1000,2000。

    我看你是新手。如果你觉得有答案解决了问题,请点击绿色复选标记,将其标记为“已接受”。我在另一个线程中看到关于将数据传递到
    JdbcCursorItemReader
    的问题你没有标记它,你说它成功地解决了问题。感谢你的回答和解决了所有问题。我不理解第1点,你能建议我如何实现你的建议吗?如果我将corePoolSize和maxPoolSize设置为20,将partitioner grid size设置为10,当Number分区代码的r是100,它会创建100个线程,每个线程按顺序处理每个代码特定数量的记录吗?我用我的配置详细信息更新了问题。如何获得代码列表,以及有没有办法通过分区器中的每个代码预先找出您有多少项?我接受了这两个答案,谢谢。我要么从t获取able或属性文件,除非我根据分区代码在DB中查询我的主表,否则我无法预先找到每个代码的记录/项数。现在我将此代码传递给itemreader,然后在那里执行此查询。我添加了关于网格大小和trhead池的更好解释,希望它有所帮助。在您的示例中,您将获得10个分区,共10部分对于第一部分,我看不到一种方法,我认为您可以通过查询数据库中的count only预先获取每个分区代码的记录数。