Spring 주석 @Transactional의 실패에 대해 이야기해 봅시다 | JD Cloud 기술 팀

I. 소개

음, 다시 구덩이를 밟는다. 이번 수요는 주로 기한이 지난 계산에 대한 수요 작업을 최적화하기 위한 것이며 기존 계산 작업은 실행하는 데 너무 오래 걸립니다. 이번에는 문제를 간략히 설명합니다: 프로젝트에서 여러 데이터베이스에 대한 작업을 수행할 때 우리가 기대하는 것은 전체 트랜잭션을 트랜잭션으로 캡슐화하는 것입니다(모두 성공 또는 모두 실패). 첫 번째 데이터의 상태 업데이트는 성공하지만 삽입 후속 데이터의 비정상 데이터 테이블을 쿼리한 후 데이터 상태가 성공적으로 업데이트되었음을 ​​발견했습니다 .

음, 코드를 확인하고 @Transactional 주석이 실제로 사용된 것을 발견했습니다. 묻지 않았습니다. 그래서 관련 정보를 인터넷으로 조회해본 결과, Spring에서 @Transactional 트랜잭션 애노테이션을 사용할 때 여러 시나리오에서 애노테이션이 실패하는 것, 즉 예상대로 트랜잭션 연산으로 캡슐화할 수 없다는 것을 알게 되어서 주석을 달고 관련 실패 시나리오를 분석했습니다. , 다음과 같이 기사를 구성하십시오.

2. @Transactional 주석 실패 시나리오 인스턴스 검증

1. @Transactional 주석 속성

속성 유형 설명하다
사용할 트랜잭션 관리자를 지정하는 선택적 자격 설명자
번식 열거형:전파· 선택적 트랜잭션 전파 동작 설정
격리 열거형: 격리 선택적 트랜잭션 격리 수준 설정
읽기 전용 부울 읽기-쓰기 또는 읽기 전용 트랜잭션, 기본 읽기-쓰기
타임아웃 정수 트랜잭션 제한 시간 설정
롤백용 Class 객체의 배열, Throwable에서 상속해야 함 트랜잭션을 롤백하게 만든 예외 클래스의 배열
rollbackForClassName 클래스 이름의 배열, Throwable에서 상속해야 함 트랜잭션을 롤백하게 만든 예외 클래스 이름의 배열
noRollbackFor Class 객체의 배열, Throwable에서 상속해야 함 트랜잭션을 롤백하지 않는 예외 클래스의 배열
noRollbackForClassName 클래스 이름의 배열, Throwable에서 상속해야 함 트랜잭션 롤백을 일으키지 않는 예외 클래스 이름의 배열

2, propagation 속성

전파는 트랜잭션의 전파 동작을 나타내며 기본값은 Propagation.REQUIRED 입니다.

속성 설명하다
전파.필수 트랜잭션이 현재 존재하면 조인하고, 존재하지 않으면 새 트랜잭션 생성(기본값)
전파.지원 현재 트랜잭션이 있는 경우 트랜잭션에 참여하고 없으면 트랜잭션이 아닌 방식으로 계속합니다.
전파.필수 현재 트랜잭션이 있는 경우 트랜잭션에 참여하고 없으면 예외를 throw합니다.
전파.REQUIRES_NEW 새로운 트랜잭션을 재생성합니다. 현재 트랜잭션이 있으면 현재 트랜잭션은 미정입니다.
전파.NOT_SUPPORTED Non-transactional 방식으로 실행, 현재 트랜잭션이 있으면 현재 트랜잭션이 잠정적으로
전파.NEVER 비트랜잭션 방식으로 실행하고 현재 트랜잭션이 있는 경우 예외를 throw합니다.
전파.중첩됨 Propagation.REQUIRED와 동일한 효과

3. @Transactional 주석의 사용 시나리오는 무엇입니까?

@Transactional 주석은 인터페이스, 클래스 및 클래스 메서드에 적용할 수 있습니다.

  • 클래스로 사용하면 이 클래스의 모든 퍼블릭 메서드가 동일한 트랜잭션 속성 정보로 구성됨을 의미합니다.

  • 메소드로 사용될 때 클래스가 @Transactional 어노테이션으로 구성되고 메소드도 @Transactional로 구성되면 메소드의 트랜잭션이 클래스의 트랜잭션 구성 정보를 대체합니다.

  • 인터페이스로 사용될 때 인터페이스에서 @Transactional을 사용하고 CGLib 동적 프록시를 사용하도록 Spring AOP를 구성하면 실패하기 때문에 권장되지 않습니다.

4. @Transactional 주석 실패 시나리오?

  • 비공개로 수정된 메서드에 @Transactional 주석을 적용하면 유효하지 않습니다.

실패 이유: Spring AOP 프록시에서 TransactionInterceptor (트랜잭션 인터셉터)는 대상 메서드 실행 전후에 인터셉트 하고 DynamicAdvisedInterceptor (CglibAopProxy의 내부 클래스) 의 Intercept 메서드 나 JDKDynamicAopProxyinvoke 메서드는 간접적으로 의 computeTransactionAttribute 메서드 를 호출한다. @Transactional 주석을 얻기 위한 AbstractFallbackTransationAttributeSource 트랜잭션 구성 정보입니다.

1 protected TransactionAttribute computeTransactionAttribute(Method method,
2    Class<?> targetClass) {
3        // Don't allow no-public methods as required.
4        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
5        return null;
6    }


이 메서드는 대상 메서드의 수정자가 public 인지 확인 하고 @transactional의 속성 구성 정보는 비공개 범위에서 가져오지 않습니다 . 그 중 @Transactional 어노테이션은 protected 및 private 수정 메소드 에 사용되며 트랜잭션은 유효 하지 않지만 오류는 보고되지 않습니다.

  • @Transactional 주석 속성 전파 설정 오류로 인해 주석이 실패합니다.

실패 이유: 구성 오류, PROPAGATION_SUPPORTS, PROPAGATION_NOT_SUPPORTED 및 PROPAGATION_NEVER의 세 가지 트랜잭션 전파 방법이 롤백되지 않습니다.

▪ 검증 예: 테스트용 데모를 작성했습니다. 데모의 주요 기능은 다음과 같습니다. 두 가지 데이터베이스 삽입 작업을 수행하고 확장 정보 필드에 메모를 추가합니다.

실행 결과는 다음과 같으며 구성된 주문 번호가 존재하지 않고 주문 쿼리가 비어 있어 예외 발생 데이터베이스를 관찰한 결과 첫 번째 데이터베이스 삽입 작업이 성공적으로 수행되었으므로 @의 검증 트랜잭션 주석이 유효하지 않습니다 .

  • @Transactional 주석 속성 rollbackFor가 잘못 설정되어 주석이 실패합니다.

rollbackFor는 트랜잭션 롤백을 트리거할 수 있는 예외 유형을 지정할 수 있습니다. 기본적으로 Spring은 확인되지 않은 예외(RuntimeException에서 상속됨) 또는 오류를 발생시켜 트랜잭션을 롤백합니다. 트랜잭션에서 다른 유형의 예외가 발생했지만 Spring이 트랜잭션을 롤백할 것으로 예상하는 경우 rollbackFor 속성을 지정해야 합니다. 그렇지 않으면 실패합니다.

  • 동일한 클래스의 메소드 호출로 인해 @Transactional이 실패함

예를 들어 클래스 demo에는 메서드 A와 B가 있고 메서드 B에는 @Transactional 애노테이션이 사용됩니다. 메서드 A에는 애노테이션이 없지만 데모 클래스는 메서드 A를 통해 메서드 B를 호출합니다. 이러한 간접 호출은 @Transactional 메서드 B의 트랜잭션 주석이 실패합니다.

실패 이유 : 트랜잭션 메소드가 현재 클래스가 아닌 다른 코드에 의해 호출되는 경우에만 Spring에 의해 생성된 프록시 객체 관리가 있을 것입니다. (Spring AOP 프록시 메커니즘으로 인해 발생).

검증 예시 : 데모의 시공 장면은 같은 클래스에 있고 테스트 메소드에 @Transactional 주석을 추가하고 querRiskScore 메소드에 이 주석을 추가하지 않은 다음 querRiskScore 메소드에서 테스트 메소드를 호출하여 여러 개인지 관찰하십시오. 예외로 인해 롤백이 중단되었습니다.


▪ 실행 결과는 다음과 같으며 구성된 주문 번호가 존재하지 않고 주문 쿼리가 비어있어 예외가 발생 데이터베이스를 관찰 한 결과 첫 번째 데이터베이스 삽입 작업은 성공적으로 수행되었지만 두 번째 데이터 삽입 작업은 성공적으로 수행 되었음을 알 수 있습니다. 실패했으며 예외로 인해 트랜잭션 작업이 트리거되지 않았으므로 검증 @Transactional 주석 메서드 간의 호출이 유효 하지 않습니다 .

  • 다중 스레드 작업으로 인해 @Transaction 사례가 실패할 수 있음

실패 원인 : 스레드는 Spring에서 관리하지 않기 때문에 스레드는 기본적으로 Spring 트랜잭션을 사용할 수 없으며 Spring이 주입된 Bean을 얻을 수 없습니다. 트랜잭션에 의해 제어되지 않습니다. .

  • 메소드의 catch에 의해 예외가 포착되어 @Transactional이 실패합니다.

예를 들어 메서드 B 내부에서 예외가 발생했는데 이때 메서드 A가 메서드 B의 예외를 try-catch하면 트랜잭션을 정상적으로 롤백할 수 없습니다.

실패 이유 : 메소드 B에서 예외가 발생한 후 현재 트랜잭션을 롤백해야 하지만 메소드 A에서는 예외를 수동으로 캡처하여 처리하기 때문에 메소드 A는 현재 트랜잭션이 정상적으로 커밋되어야 한다고 생각하고, Throw org.springframework.transaction.UnexpectedRollbackException: 트랜잭션이 롤백 전용 예외로 표시되었기 때문에 롤백 되었습니다 .

▪인스턴스 검증 : 이 시나리오의 핵심은 예외가 포착되어 정상적으로 throw되지 않아 @Transactional 어노테이션이 제대로 작동하지 않는 것이므로 데모 인스턴스 시나리오를 단순화하여 다음과 같이 시나리오를 구성했습니다. querRiskScore 메서드로 이동한 다음 querRiskScore 메서드에서 예외를 캐치하고, 여러 삽입 작업이 예외로 인해 롤백을 중단하는지 여부를 관찰합니다.


▪ 실행 결과는 다음과 같이 생성된 주문번호가 존재하지 않고 주문 쿼리가 비어있는 상태에서 예외가 발생하지만 메서드 내부에서 예외를 포착하여 상위 레이어로 던지지 않음 우리가 예상하는 시나리오 데이터 삽입 실행이 두 번 실패한다는 것입니다. 그러나 데이터베이스를 관찰한 결과 첫 번째 데이터베이스 삽입 작업이 성공적으로 실행 되고 두 번째 데이터 삽입 작업이 성공적으로 실행되어 예상 결과와 일치하지 않으므로 다음을 확인하십시오 . 메서드에서 예외가 발생한 장면에서 @Transactional 주석이 실패 합니다 .

이유 : Spring의 트랜잭션은 Business Method가 호출되기 전에 시작되고 Commit 또는 Rollback은 Business Method가 실행된 후에 실행됨 트랜잭션 실행 여부는 런타임 예외 발생 여부에 따라 다름 런타임 예외가 발생한 경우 및 귀하의 비즈니스 방법 캐치가 없으면 트랜잭션이 롤백됩니다.

3. "사업" 지식 복습

1. 거래란?

트랜잭션(Transaction)은 시스템에서 데이터에 액세스하고 업데이트하는 일련의 작업으로 구성된 프로그램 실행 논리 단위(Unit)입니다.

일반적으로 트랜잭션은 데이터베이스 트랜잭션을 의미하며 데이터베이스 트랜잭션을 사용하면 다음과 같은 두 가지 이점이 있습니다.

  • 여러 응용 프로그램이 동시에 데이터베이스에 액세스할 때 트랜잭션은 서로의 작업이 서로 간섭하지 않도록 이러한 응용 프로그램 간에 격리 방법을 제공할 수 있습니다 .

  • 트랜잭션은 일련의 데이터베이스 작업이 장애에서 정상 상태로 복구하는 방법을 제공하는 동시에 데이터베이스가 비정상 상태 에서도 데이터 일관성을 유지할 수 있는 방법을 제공합니다.

2. 트랜잭션의 특징은 무엇입니까?

트랜잭션의 ACID 특성이라고 하는 원자성, 일관성, 격리성 및 지속성.

  • 원자성

트랜잭션의 원자성은 트랜잭션이 원자적 작업 시퀀스 단위여야 함을 의미합니다. 즉, 트랜잭션에 포함된 각 작업은 한 번의 실행 동안 두 가지 상태로만 나타납니다. 모두 성공적으로 실행되고 아무 것도 실행되지 않습니다. 어떤 연산이 실패하면 전체 트랜잭션이 실패하게 되고 실행된 다른 연산은 실행 취소되고 롤백되며, 모든 연산이 성공해야만 전체 트랜잭션이 성공적으로 완료된 것으로 간주됩니다.

  • 일관성

트랜잭션의 일관성은 트랜잭션의 실행이 데이터베이스 데이터의 무결성과 일관성을 파괴할 수 없음을 의미하며 트랜잭션 실행 전후에 데이터베이스는 일관된 상태여야 합니다. 즉, 트랜잭션 실행의 결과는 데이터베이스를 하나의 일관된 상태에서 다른 상태로 전환해야 하므로 데이터베이스가 성공적인 트랜잭션 커밋의 결과만 포함하는 경우 일관된 상태에 있다고 합니다. 그리고 데이터베이스 시스템이 운영 중에 실패하면 일부 트랜잭션은 완료되기 전에 강제로 중단되고 이러한 미완성 트랜잭션으로 인해 데이터베이스에 대한 일부 수정 사항이 물리적 데이터베이스에 기록되어 데이터베이스가 잘못된 상태가 됩니다. , 또는 일관성 없는 상태입니다.

  • 격리

트랜잭션 격리는 동시 환경에서 동시 트랜잭션이 서로 격리되고 트랜잭션 실행이 다른 트랜잭션에 의해 방해받을 수 없음을 의미합니다. 즉, 서로 다른 트랜잭션이 동일한 데이터를 동시에 조작할 때 각 트랜잭션은 자체의 완전한 데이터 공간을 갖습니다. 즉, 트랜잭션 내에서 사용되는 작업 및 데이터는 다른 동시 트랜잭션과 격리되며 동시에 실행되는 트랜잭션 간의 트랜잭션은 간섭할 수 없습니다 서로 서로 함께.

  • 고집

트랜잭션이 커밋되면 수정 사항은 데이터베이스에 영구적으로 저장되며 데이터베이스가 실패하더라도 영향을 미치지 않아야 합니다. 트랜잭션의 내구성은 100% 내구성이 될 수 없으며 트랜잭션 자체의 관점에서만 보장될 수 있으며 일부 외부 요인으로 인해 하드 디스크 손상과 같은 데이터베이스 오류가 발생하면 제출된 모든 데이터가 길을 잃다 .

3. Spring에서 트랜잭션이란?

Spring은 또한 우수한 트랜잭션 관리 메커니즘을 제공하며 주로 프로그래밍 트랜잭션선언 트랜잭션 으로 나뉩니다 .

  • 프로그래밍 방식의 트랜잭션

코드에서 트랜잭션 제출, 롤백 및 기타 작업을 수동으로 관리하는 것을 말하며 코드는 상대적으로 방해가 됩니다. 프로그래밍 방식의 트랜잭션 방식은 개발자가 코드에서 열기, 커밋 및 롤백과 같은 트랜잭션을 수동으로 관리해야 합니다.

public void test() {

      TransactionDefinition def = new DefaultTransactionDefinition();

      TransactionStatus status = transactionManager.getTransaction(def);

      try {

         // 事务操作

         // 事务提交

         transactionManager.commit(status);

      } catch (DataAccessException e) {

         // 事务提交

         transactionManager.rollback(status);

         throw e;

      }

}


  • 선언적 트랜잭션

선언적 트랜잭션은 특정 비즈니스와 트랜잭션 처리를 분리하는 AOP 기반의 Aspect 지향이며, 코드의 간섭이 매우 낮아 실제 개발에서 일반적으로 사용됩니다. 우리는 TX와 AOP의 xml 구성 파일 방식과 @Transactional 주석 방식을 자주 사용합니다.

▪선언적 거래의 장점:

코드에 영향을 주지 않으며 메소드에 비즈니스 로직만 작성하면 되므로 많은 코드를 절약할 수 있습니다.

▪선언적 트랜잭션의 단점:

1. 선언적 트랜잭션의 세분성: 선언적 트랜잭션의 한계메서드에 최소 세분성을 적용해야 하며 장기높은 동시성 시나리오 에는 적합하지 않습니다 .

2. 선언적 트랜잭션은 개발자가 간과하기 쉽습니다.트랜잭션 내포 방식에서 RPC 원격 호출, MQ 전송, Redis 업데이트, 파일 쓰기 및 기타 작업이 있을 때 다음 시나리오가 존재할 수 있습니다.

▪ 트랜잭션 내포 방식에서 RPC 호출은 성공하지만 로컬 트랜잭션 롤백으로 인해 RPC 호출이 롤백되지 않습니다(분산 트랜잭션은 아직 논의되지 않음).

▪트랜잭션 내포 방식의 원격 호출은 전체 트랜잭션 주기를 연장하여 트랜잭션의 데이터베이스 연결을 일관되게 점유하고 유사한 작업이 너무 많으면 데이터베이스 연결 풀이 고갈됩니다.

3. 선언적 트랜잭션을 잘못 사용하면 일부 시나리오에서 오류가 발생할 수 있습니다 .

4. 요약

저자: Jingdong Technology Song Huichao

출처: JD 클라우드 개발자 커뮤니티

RustDesk 1.2: Flutter를 사용하여 데스크톱 버전을 재작성하여 Wayland의 주장된 GPT-4 모델 아키텍처 유출 지원 : 혼합 전문가 모델(MoE)을 사용하여 1조 8천억 개의 매개 변수 포함 Open to all" Rust 1.71.0 안정 릴리스 React Angular.js 순간이 있습니까? Microsoft, Calibri를 대체할 새로운 기본 글꼴인 Aptos 출시Microsoft : Windows 11에서 Rust IntelliJ IDEA 2023.1.4 릴리스 를 사용하기 위한 노력 증가
{{o.이름}}
{{이름}}

추천

출처my.oschina.net/u/4090830/blog/10089156