Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 多个Spring批处理作业并发执行,导致Spring批处理元数据表中出现死锁_Java_Spring_Spring Batch - Fatal编程技术网

Java 多个Spring批处理作业并发执行,导致Spring批处理元数据表中出现死锁

Java 多个Spring批处理作业并发执行,导致Spring批处理元数据表中出现死锁,java,spring,spring-batch,Java,Spring,Spring Batch,我们有多个Spring批处理作业,每个作业都使用CommandLineJobRunner在自己的java实例中运行。所有作业都同时启动,仅读/写平面文件并更新SQL Server中托管的相同Spring批处理元数据。唯一涉及的数据库是Spring批处理元数据数据库 当多个作业同时启动时,会出现SQL死锁异常。下面可以找到更详细的堆栈跟踪。从数据库的角度来看,我们可以看到死锁受害者正在执行以下操作之一:将默认值插入BATCH_JOB_SEQ或从BATCH_JOB_SEQ中删除,其中ID

我们有多个Spring批处理作业,每个作业都使用CommandLineJobRunner在自己的java实例中运行。所有作业都同时启动,仅读/写平面文件并更新SQL Server中托管的相同Spring批处理元数据。唯一涉及的数据库是Spring批处理元数据数据库

当多个作业同时启动时,会出现SQL死锁异常。下面可以找到更详细的堆栈跟踪。从数据库的角度来看,我们可以看到死锁受害者正在执行以下操作之一:将默认值插入BATCH_JOB_SEQ或从BATCH_JOB_SEQ中删除,其中ID<某个编号

我们使用默认的MapJobRegistry,或者使用默认的作业存储库,或者指定JobRepositoryFactoryBean。对于用于与Spring批处理数据库交互的数据源,我们已经使用标准的Microsoft SQL Server SQLServerDriver尝试了DriverManager数据源或DBCP2池BasicDataSource。我可以上传更多特定的配置文件,但在我的测试中,只要使用SQL Server和标准Spring配置,就会出现问题

在我的调查中,我认为问题是由于默认的递增类org.springframework.jdbc.support.incrementer.SqlServerMaxValueIncrementer如何与SQL Server数据库表的构造方式一起递增作业和步骤实例ID。SqlServerMaxValueIncrementer中的代码是同步的,因此,如果我们在同一个Java实例中运行所有作业,这将不是一个问题

如果我们在DB2数据库中实现Spring批处理元数据,就不会有问题。SQLServer实现使用实际的表,DB2实现使用序列对象

有人碰到过这个问题吗?我是不是错过了什么?似乎每当我们遇到这样的问题时,它就像在yyy中设置xxx一样简单。如果没有,有人知道为什么SpringBatch没有在SQLServer实现中实现sequence对象吗

堆栈跟踪:

[org.springframework.batch.core.launch.support.CommandLineJobRunner] - <Job Terminated in error: Could not increment identity; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: Transaction (Process ID 74) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.>
org.springframework.dao.DataAccessResourceFailureException: Could not increment identity; 
nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: 
Transaction (Process ID 74) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
        at org.springframework.jdbc.support.incrementer.SqlServerMaxValueIncrementer.getNextKey(SqlServerMaxValueIncrementer.java:124)
        at org.springframework.jdbc.support.incrementer.AbstractDataFieldMaxValueIncrementer.nextLongValue(AbstractDataFieldMaxValueIncrementer.java:1
28)
        at org.springframework.batch.core.repository.dao.JdbcJobInstanceDao.createJobInstance(JdbcJobInstanceDao.java:108)
        at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:135)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
[org.springframework.batch.core.launch.support.CommandLineJobRunner]-
org.springframework.dao.DataAccessResourceFailureException:无法增加标识;
嵌套异常为com.microsoft.sqlserver.jdbc.SQLServerException:
事务(进程ID 74)在另一个进程的锁资源上被死锁,并被选为死锁牺牲品。重新运行事务。
位于org.springframework.jdbc.support.incrementer.SqlServerMaxValueIncrementer.getNextKey(SqlServerMaxValueIncrementer.java:124)
位于org.springframework.jdbc.support.incrementer.AbstractDataFieldMaxValueIncrementer.nextLongValue(AbstractDataFieldMaxValueIncrementer.java:1
28)
位于org.springframework.batch.core.repository.dao.JdbcJobInstanceDao.createJobInstance(JdbcJobInstanceDao.java:108)
位于org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:135)
在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)处
位于sun.reflect.NativeMethodAccessorImpl.invoke(未知源)
配置:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:batch="http://www.springframework.org/schema/batch"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
    http://www.springframework.org/schema/batch
    http://www.springframework.org/schema/batch/spring-batch.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
    lazy-init="true">
    <property name="dataSource" ref="batchPoolingDataSource" />
</bean>

<bean id="jobRegistry"
    class="org.springframework.batch.core.configuration.support.MapJobRegistry" />

<bean id="jobRegistryBeanPostProcessor"
    class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
    <property name="jobRegistry" ref="jobRegistry" />
</bean>

<bean id="jobRepository"
    class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
    <property name="databaseType" value="SQLSERVER" />
    <property name="dataSource" ref="batchPoolingDataSource" />
    <property name="transactionManager" ref="transactionManager" />
</bean>

<bean id="jobLauncher"
    class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
    <property name="jobRepository" ref="jobRepository" />
</bean>

<bean id="jobExplorer"
    class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
    <property name="dataSource" ref="batchPoolingDataSource" />
</bean>

<bean id="jobOperator"
    class="org.springframework.batch.core.launch.support.SimpleJobOperator">
    <property name="jobExplorer" ref="jobExplorer" />
    <property name="jobLauncher" ref="jobLauncher" />
    <property name="jobRegistry" ref="jobRegistry" />
    <property name="jobRepository" ref="jobRepository" />
</bean>

<bean class="org.springframework.batch.core.scope.StepScope">
    <property name="proxyTargetClass" value="true" />
</bean>

<bean id="batchPoolingDataSource"  class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
    <property name="url" value="jdbc:sqlserver://server info" />
    <property name="username" value="${batch.jdbc.user}" />
    <property name="password" value="${batch.jdbc.password}" />
    <property name="initialSize" value="5" />
    <property name="maxTotal" value="15" />
    <property name="maxWaitMillis" value="5000" />
</bean>

<bean id="batchDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
    <property name="driverClassName" value="org.springframework.jdbc.datasource.DriverManagerDataSource" />
    <property name="url" value="jdbc:sqlserver://server info" />
    <property name="username" value="${batch.jdbc.user}" />
    <property name="password" value="${batch.jdbc.password}" />
</bean>


在进一步研究这一点之后,我在开发支持
JobRepository
的DAO版本以及使用SQL Server
IDENTITY
而不是序列的过程中找到了解决这一问题的方法,而不需要太多的配置

解决此问题的简单方法是配置
databaseType
isolationLevelForCreate
作业存储库的属性。以下是我在SQL Server 2008中使用的设置:

<bean id="jobRepository"
    class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="transactionManager" ref="transactionManager" />
    <property name="databaseType" value="SQLSERVER" />
    <property name="isolationLevelForCreate" value="ISOLATION_REPEATABLE_READ" />
</bean>

我已经用一组Quartz作业启动了30个作业(同一个作业有不同的参数)对此进行了测试,到目前为止,我还没有看到任何问题

在启动作业时,我还保留了重试代码(请参阅对问题的评论),以便捕获任何可能的死锁并允许其重试。这可能是一个没有实际意义的观点,但我不能冒险让乔布斯失败


我认为,在Spring批处理文档中提及这些设置对于其他人来说是非常有帮助的,这些设置涉及在使用SQL Server作为数据源时在给定时间启动多个作业。再说一次,我想没有多少人会使用SQL Server。

您能提供您的事务配置设置(以及任何SQL Server配置)吗?至于为什么SpringBatch不在SqlServer中使用序列,这是因为历史上没有序列(序列是SqlServer 2012中的一项新功能)。添加了配置信息关于解决此问题的任何其他信息?我也面临着同样的问题,我使用了一个带有指数随机退避策略的RetryTemplate来尝试从中恢复。它工作正常,但效果很差。我们最终切换到了DB2数据库。添加到我的注释中..被转移了。我们最终切换到了一个DB2数据库,这就是为什么我没有将其标记为“已回答”或“已放弃”。根本问题在于SQL server处理密钥的方式。DB2模式将有问题的表定义为(BATCH_STEP_EXECUTION_SEQ、BATCH_JOB_EXECUTION_SEQ、BATCH_JOB_SEQ)序列表,其中SQLServer ddl仅将这些表定义为具有一列的常规表。SQLServer确实有序列选项,我猜他们只是从来没有费心去更新它。