为什么分区步骤等待数据库连接?

时间:2013-11-08 15:56:23

标签: java spring spring-batch apache-commons-dbcp

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

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

我最初的想法是,我应该至少有gridSize个数据库连接,这样每个分区步骤都有自己的数据库连接,加上一些额外的Spring可能有的任何开销(不是一个非常科学的措施我知道...因此问题)。

我在这个配置中观察到的是我的分区步骤会花费大量时间来阻止等待数据库连接。

以下是使用spring-batch-samples中的partitionJdbcJob来说明场景的示例:

数据源:

<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 max ...这似乎很多)。然后每个步骤一次翻阅它的工作100条记录,在每个页面之后提交工作。

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

1 个答案:

答案 0 :(得分:0)

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

Spring批处理自动处理批处理作业的JDBC连接和事务。步骤开始时,将获得连接并打开事务。当步骤结束时,提交事务并关闭连接(返回池)。在该步骤的持续时间内,在开始时获得的连接被认为是“在使用中”。

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

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

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

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

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

没有更多被阻止的线程!