복붙노트

[SPRING] Spring Batch 메타 데이터 테이블에서 동시에 교착 상태가 발생하는 여러 Spring Batch 작업

SPRING

Spring Batch 메타 데이터 테이블에서 동시에 교착 상태가 발생하는 여러 Spring Batch 작업

우리는 CommandLineJobRunner를 사용하여 각각의 자바 인스턴스에서 실행되는 여러 개의 Spring Batch 작업을 가지고있다. 모든 작업이 동시에 시작되고 플랫 파일을 읽고 쓰고 SQL Server에서 호스팅되는 동일한 스프링 배치 메타 데이터를 업데이트합니다. 관련된 유일한 데이터베이스는 Spring Batch 메타 데이터 데이터베이스입니다.

여러 작업이 동시에 시작되면 SQL 데드락 예외가 발생합니다. 더 자세한 스택 추적은 아래에서 찾을 수 있습니다. 데이터베이스 관점에서 보았을 때 데드락 피해자는 BATCH_JOB_SEQ 기본값으로 삽입하거나 BATCH_JOB_SEQ에서 삭제 (ID

우리는 기본 MapJobRegistry와 기본 작업 저장소 또는 JobRepositoryFactoryBean을 사용하고 있습니다. 스프링 배치 데이터베이스와 상호 작용하는 데 사용되는 데이터 소스의 경우 표준 Microsoft SQL Server SQLServerDriver를 사용하여 DriverManagerDataSource 또는 DBCP2 풀링 BasicDataSource를 모두 시도했습니다. 더 구체적인 설정 파일을 업로드 할 수는 있지만 SQL Server와 표준 Spring 구성을 사용하는 한 내 테스트에서 문제가 발생합니다.

내 조사에서이 문제는 기본 증분 클래스 인 org.springframework.jdbc.support.incrementer.SqlServerMaxValueIncrementer가 SQL Server 데이터베이스 테이블 구성 방법과 함께 작업 및 단계 인스턴스 ID를 증가시키는 방법 때문이라고 생각합니다. SqlServerMaxValueIncrementer의 코드는 동기화되어 있으므로 동일한 Java 인스턴스에서 모든 작업을 실행 중이면 문제가되지 않습니다.

DB2 데이터베이스에 스프링 배치 메타 데이터를 구현하면 문제가 없습니다. SQL Server 구현은 실제 테이블을 사용하고 DB2 구현은 시퀀스 객체를 사용합니다.

이 문제가있는 사람이 있습니까? 방금 뭔가 빠졌나요? 이런 문제가 발생할 때마다 yyy에 xxx를 설정하는 것처럼 간단합니다. 그렇지 않다면 스프링 배치가 SQL Server 구현에서 시퀀스 객체를 구현하지 않는 이유를 아는 사람이 있습니까?

스택 추적 :

[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)

구성 :

<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>

해결법

  1. ==============================

    1.이 부분을 더 연구하고 부분적으로 JobRepository를 백업하고 시퀀스 대신 SQL Server IDENTITY로 작업하는 DAO 버전 작업에 대해 부분적으로 살펴본 후 약간의 구성만으로이 문제를 해결할 수있는 방법을 찾았습니다.

    이 부분을 더 연구하고 부분적으로 JobRepository를 백업하고 시퀀스 대신 SQL Server IDENTITY로 작업하는 DAO 버전 작업에 대해 부분적으로 살펴본 후 약간의 구성만으로이 문제를 해결할 수있는 방법을 찾았습니다.

    이 문제를 해결하는 간단한 방법은 JobRepository의 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 가지 작업 (다른 매개 변수로 동일한 작업)을 테스트했으며 지금까지 어떤 문제도 보지 못했다.

    가능한 교착 상태를 잡아서 다시 시도 할 수 있도록 작업을 시작할 때 재 시도 코드 (질문에 대한 주석 참조)를 그대로 유지했습니다. 그것은 논점 일지 모르지만 나는 일자리를 발사하지 못하게 될 위험이 없습니다.

    SQL Server를 데이터 소스로 사용할 때 주어진 시간에 여러 작업을 시작하는 것과 관련된 Spring Batch 문서에서 이러한 설정을 언급하는 것이 다른 사람들에게 많은 도움이 될 것이라고 생각합니다. 그럼 다시, 나는 많은 사람들이 SQL Server에 갇혀 있지 않다고 생각합니다.

  2. from https://stackoverflow.com/questions/26530205/multiple-spring-batch-jobs-executing-concurrently-causing-deadlocks-in-the-sprin by cc-by-sa and MIT license