0

我有一个使用分区步骤的 Spring Batch (v2.2.1) 作业。此分区步骤使用JpaPagingItemReader对返回的结果进行分页来访问数据库。分区在本地 JVM 中使用 Spring 的SimpleAsyncTaskExecutor.

假设数据库操作需要“长”时间(这里的长意味着比处理时间长),我的问题归结为:确定最大数据库连接数以防止分区阻塞等待的好的经验法则是什么连接?

我最初的想法是我应该至少有gridSize数据库连接,以便每个分区步骤都有自己的数据库连接可以使用,再加上一些额外的用于 Spring 可能具有的任何开销(我知道这不是一个非常科学的衡量标准......因此问题) .

我在此配置中观察到的是,我的分区步骤将花费大量时间阻塞等待数据库连接。

这是一个使用partitionJdbcJobspring-batch-samples 来说明场景的示例:

数据源:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${jdbc.driver}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.user}" />
    <property name="password" value="${jdbc.password}" />
    <property name="validationQuery" value=""/>
    <property name="testWhileIdle" value="false"/>
    <property name="maxActive" value="7"/>
</bean>

作业、分区程序和分区步骤:

<job id="partitionJdbcJob" xmlns="http://www.springframework.org/schema/batch">
    <step id="step">
        <partition step="step1" partitioner="partitioner">
            <handler grid-size="5" task-executor="taskExecutor"/>
        </partition>
    </step>
</job>

<bean id="partitioner" class="org.springframework.batch.sample.common.ColumnRangePartitioner">
    <property name="dataSource" ref="dataSource" />
    <property name="table" value="CUSTOMER" />
    <property name="column" value="ID" />
</bean>

<bean id="taskExecutor" class="org.springframework.core.task.SimpleAsyncTaskExecutor" />

<step id="step1" xmlns="http://www.springframework.org/schema/batch">
    <tasklet>
        <chunk writer="itemWriter" reader="itemReader" processor="itemProcessor" commit-interval="100" />
        <listeners>
            <listener ref="fileNameListener" />
        </listeners>
    </tasklet>
</step>

<bean id="itemReader" class="org.springframework.batch.item.database.JpaPagingItemReader" scope="step">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
    <property name="pageSize" value="100"/>
    <property name="queryProvider">
        <bean class="my.example.QueryProvider" scope="step">
            <property name="minDefaultId" value="#{stepExecutionContext[minValue]}" />
            <property name="maxDefaultId" value="#{stepExecutionContext[maxValue]}" />
        </bean>
    </property>
</bean>

鉴于上述配置,以下是重要的数字:

  • 网格大小:5
  • 最大活动连接数:7
  • 提交间隔:100
  • 页面大小:100

使用此配置,我希望分区程序可以分区 5 个步骤。每个步骤都应该能够拥有自己的数据库连接(因为应用程序最多允许 7 个......这似乎很多)。然后每一步将一次翻阅它的工作 100 条记录,在每页之后提交工作。

但是,我观察到(使用 jconsole)执行我的分区步骤的线程会经常被阻塞以等待数据库连接。如果我的最大活动连接数大于我的网格大小,他们为什么会阻止?

4

1 回答 1

0

我发现实际上,您需要最少的gridSize * 2 + 1数据库连接。关于为什么这是通过观察(大量调试),我得出了以下结论。因此,如果我的某些假设不正确,请纠正我。

Spring batch 自动处理批处理作业的 JDBC 连接和事务。当一个 Step 开始时,将获得一个连接并打开一个事务。当步骤完成时,事务被提交并关闭连接(返回池)。在该步骤的持续时间内,开始时获得的连接被视为“正在使用”。

除了为 Step 打开的事务和连接之外,JpaPagingItemReader还需要一个事务来执行它的分页。这也需要一个新的连接。事务/连接在读取页面之前获得,写入之后提交/关闭。

由于(在我的场景中)我正在划分 5 个步骤,每个步骤都有它自己的JpaPagingItemReader,我需要 5 个连接用于这些步骤以及 5 个连接用于读者(即gridSize * 2)。

额外的 1 个连接来自“主”步骤与分区步骤同时执行的事实;这也需要连接。

所以回到我的例子,我设置这样的数字:

  • 网格大小:5
  • 最大活动连接数:11
  • 提交间隔:100
  • 页面大小:100

并且没有更多的阻塞线程!

于 2013-11-08T15:56:23.410 回答