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

Java 为什么分区步骤等待数据库连接?,java,spring,spring-batch,apache-commons-dbcp,Java,Spring,Spring Batch,Apache Commons Dbcp,我有一个使用分区步骤的Spring批处理(v2.2.1)作业。此分区步骤使用JpaPagingItemReader访问数据库,以翻阅返回的结果。分区在本地JVM中使用Spring的SimpleAsynctaskeExecutor执行 假设数据库操作需要“很长”的时间(这里很长意味着比处理时间长),我的问题归结为:确定最大数据库连接数以防止分区在等待连接时阻塞的经验法则是什么 我最初的想法是,我应该至少有gridSize数据库连接,这样每个分区步骤都有自己的数据库连接可以使用,再加上一些额外的开销

我有一个使用分区步骤的Spring批处理(v2.2.1)作业。此分区步骤使用
JpaPagingItemReader
访问数据库,以翻阅返回的结果。分区在本地JVM中使用Spring的
SimpleAsynctaskeExecutor
执行

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

我最初的想法是,我应该至少有
gridSize
数据库连接,这样每个分区步骤都有自己的数据库连接可以使用,再加上一些额外的开销(我知道这不是一个非常科学的测量方法……因此问题就来了)

通过这种配置,我观察到我的分区步骤将花费大量时间等待数据库连接

下面是一个使用spring批处理示例中的
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个…这似乎足够了)。然后,每个步骤将一次翻阅其工作100条记录,并在每页之后提交工作


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

我发现,事实上,您需要最小的
gridSize*2+1
数据库连接数。我通过观察(大量调试)得出以下结论。因此,如果我的一些假设不正确,请纠正我

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

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

由于(在我的场景中)我划分了5个步骤,每个步骤都有自己的
JpaPagingItemReader
,因此我需要5个步骤连接以及5个读取器连接(即
gridSize*2

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

回到我的例子,我设置了如下数字:

  • 网格大小:5
  • 最大活动连接数:11
  • 提交间隔:100
  • 页面大小:100
不再有阻塞线程