programing

Spring Batch ORA-08177: 단일 작업을 실행할 때 이 트랜잭션에 대한 액세스를 직렬화할 수 없습니다, 직렬화된 분리 수준

javajsp 2023. 6. 8. 19:23

Spring Batch ORA-08177: 단일 작업을 실행할 때 이 트랜잭션에 대한 액세스를 직렬화할 수 없습니다, 직렬화된 분리 수준

Spring Batch의 Job Repository에서 SERIALIZED 격리 수준과 함께 다음 예외가 발생했습니다.

org.springframework.dao.CannotSerializeTransactionException: PreparedStatementCallback; SQL [INSERT into DATAFEED_APP.BATCH_JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION) values (?, ?, ?, ?)]; ORA-08177: can't serialize access for this transaction

중첩 예외는 java.sql입니다.SQL 예외: ORA-08177: 이 트랜잭션에 대한 액세스를 직렬화할 수 없습니다.

at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:269)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:603)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:812)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:868)
at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:872)
at org.springframework.batch.core.repository.dao.JdbcJobInstanceDao.createJobInstance(JdbcJobInstanceDao.java:105)
at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:135)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.batch.core.repository.support.AbstractJobRepositoryFactoryBean$1.invoke(AbstractJobRepositoryFactoryBean.java:172)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy27.createJobExecution(Unknown Source)
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:124)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:117)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy61.run(Unknown Source)

하나의 작업만 실행하고 다른 작업은 병렬로 실행하지 않습니다.JobRepository에 대한 분리 수준을 ISOLISION_READ_COMMITmitted로 변경하면 예외가 사라집니다.

이 예외의 이유는 무엇입니까?

공식 문서에서 - 4.3.1

이 메서드에 대한 기본 분리 수준은 SERIALIZE(직렬)이며, 이는 매우 공격적입니다. READ_COMMITTED도 마찬가지로 작동합니다. READ_UNCOMMITTED는 두 프로세스가 이러한 방식으로 충돌하지 않을 가능성이 있는 경우 괜찮습니다.그러나 create* 메서드에 대한 호출은 매우 짧기 때문에 데이터베이스 플랫폼이 이 메서드를 지원하는 한 SERIALIZED가 문제를 일으킬 가능성은 거의 없습니다.

저도 같은 문제를 겪었고, 저장소 수준에서 효과적으로 격리하는 것이 핵심입니다. 다음은 제게 맞는 코드의 예입니다.

<batch:job-repository id="jobRepository"
    data-source="dataSource" transaction-manager="transactionManager"
    isolation-level-for-create="READ_COMMITTED" table-prefix="SB_" />   

직렬화된 트랜잭션을 사용하는 경우 Oracle Docs에 따라 테이블의 initrans 매개 변수를 늘려야 합니다.직렬화된 트랜잭션을 처리하려면 이 값이 3 이상이어야 합니다.

alter table BATCH_.... INITRANS 3

Spring Batch 애플리케이션(Spring Boot 2.3.3)에서도 동일한 문제가 발생했습니다.솔루션은 다음과 같습니다.

  1. 제거한다.@EnableTransactionManagementa부터@Configuration데이터 소스가 구성된 클래스입니다.(@Transactional또한 제거해야 합니다.)

  2. application.yaml에 다음을 추가합니다.

배치:리포지토리:생성에 대한 분리 수준:ISOLATION_READ_COMMITED

INI_TRANS를 100으로 끌어올리려고 시도했지만 여전히 문제가 발생했습니다.

테이블 작성에 행 종속성을 추가할 것을 제안하는 이 기사를 찾았습니다.

http://www.devx.com/dbzone/Article/41591?pf=true

INI_TRANS 및 현재 ROW Dependencies를 사용하는 저의 경우 Serialized에 대한 예외가 사라졌습니다.

업데이트: 완벽한 솔루션은 아닌 것으로 밝혀졌습니다.우리는 하룻밤 사이에 이런 일련의 예외적인 사건이 하나 있었습니다.단일 장애가 발생하기 전에 수백 번의 실행을 수행했기 때문에 훨씬 나아졌지만 ROWDEPINCES를 사용하는 것이 아직 완벽한 솔루션은 아닌 것 같습니다.

이 문제에 대한 해결책이 있습니다.

아래 단계를 따릅니다.

  1. 데이터베이스에 테이블을 수동으로 작성합니다(링크).
  2. 를 몇개의 레코를 삽합니다에 삽입합니다.BATCH_JOB_INSTANCE,BATCH_JOB_EXECUTION그리고.BATCH_JOB_EXECUTION_PARAMS테이블. (약속하는 것을 잊지 마세요)
  3. 오류가 해결되었습니다.즐거운 시간 되세요.

분리를 추가하여 이 오류를 해결할 수 있었습니다.아래와 같은 생성 수준:

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

Oracle 트랜잭션 격리 수준 "SERIALIABLEIBLE"과 함께 테이블 생성 문에 "PRIMARY KEY" 한정자가 사용된 문제를 추적할 수 있었습니다.다음과 같은 간단한 SQL 스크립트를 사용하여 이를 테스트할 수 있습니다.

CREATE TABLE test1 (
    test_id NUMBER(1) NOT NULL PRIMARY KEY
);

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO test1 VALUES ( 1 );
COMMIT;

결과: ORA-08177 오류.

근본 원인:

이 문제는 Spring Batch 코드와 관련된 것이 아니라 Oracle이 버전 11.2 이후에 테이블을 생성하는 방식과 관련이 있습니다. 자세한 내용은 여기를 참조하십시오. https://oracle-base.com/articles/11g/segment-creation-on-demand-11gr2)

일반적인 문제에 대한 자세한 설명은 https://asktom.oracle.com/pls/apex/asktom.search?tag=isolation-level-serialization 에서 확인할 수 있습니다.

사용한 솔루션:

위의 create 절에 "SEGENCE CREATION Immediate"를 추가하면 문제가 해결되었습니다.

CREATE TABLE test1 (
    test_id NUMBER(1) NOT NULL PRIMARY KEY
)
SEGMENT CREATION IMMEDIATE;

이에 따라 Spring Batch 작업 저장소에 대한 모든 "CREATE TABLE" 문에 동일한 내용을 추가했으며 작업이 잘 진행되었습니다.

database.maximumPoolSize 크기를 3에서 5로 늘리면 오류가 해결되었습니다.

저의 경우 DB 쪽에서 문제가 발생했을 때 재실행 시에도 동일한 문제가 발생합니다.

해결하기 위해 아래 테이블을 모두 정리하고 그 이후에 다시 실행해도 괜찮을 것 같습니다.

  • BATCH_JOB_EXECUTION
  • BATCH_JOB_EXECUTION_CONTACLE
  • BATCH_JOB_EXECUTION_PARAMS
  • BATCH_JOB_INSTANCE
  • 배치_스텝_실행
  • BATCH_STEP_EXECUTION_CONTACLE

언급URL : https://stackoverflow.com/questions/22364432/spring-batch-ora-08177-cant-serialize-access-for-this-transaction-when-running