본문 바로가기
  • Where there is a will there is a way.
개발/spring

spring transaction 이란

by 소확행개발자 2020. 4. 23.
나는 spring 개발을 하면서 흔히 서비스 메소드에 @Transaction 어노테이션을 보곤했다.
 처음엔 아무것도 모르고 개발을 했었지만 점차 여러가지 문제에 직면하게 되었고 그러다 보니 Transaction  에 대해서 정리가 필요함을 느꼇다.

 

 

우리가 흔히 데이터를 처리하는 과정에서 트랜잭션에 대한 내용을 개념적으로나마 배웠었다.

트랜잭션은 논리적인 개념이고 4가지의 성질을 지닌다 Atomic Consistent Isolation Durability

 

spring transaction 도 이 개념을 포함하고 있다.

spring 은 @Transactional 로 표현하는데 이는 우리가 알고있는 어노테이션 방식으로 선언되어서 해당 메소드를 시행할때 메서드 위에 트랜잭션 기능이 적용된 프록시 객체가 생성된다.

 

AbstractPlatformTransactionManager.getrTransaction() -> creating new transaction with name ( method name )

 

스프링 트랜잭션 ( transaction ) 의 성질

 

1. 스레드가 다르면 다른 트랜잭션이 생성된다.

 따라서 개발자는 비즈니스로직에 더 집중할 수 있게 된다. 

 물론 spring 은 OSIV 를 사용하기 때문에 수정할때 주의해야할 필요는 있다.

 ( 트랜잭션이 종료되면 JPA 를 사용할 경우 엔티티 매니저가 flush 를 수행한다 )

 

2. 트랜잭션 롤백과 예외

 트랜잭션은 체크 익셉션과 언체크 익셉션의 처리방식이 다르다.

 어떻게 보면 이는 당연한 성질이다. 

 

 언체크 예외일 경우는 우리가 처리할 수 없는 치명적인 에러다. 따라서 트랜잭션으로 묶인 로직은 당연히 롤백되어야 맞다.

 

반대로 체크 예외는 비즈니스 처리상 처리되는 로직이므로 적절히 처리할 능력이 된다면 해당 예외를 처리하고 트랜잭션은 롤백되지 않는게 맞다고 생각한다.

 

3. 트랜잭션 전파

 복잡한 비즈니스로직을 처리하다 보면 트랜잭션 안에서 다른 트랜잭션 메소드를 호출하는 일이 발생한다.

 이러한 경우 트랜잭션은 어떻게 처리될까 ?

 

 일반적으로는 부모 트랜잭션이 존재할 경우 자식 트랜잭션은 부모 트랜잭션에 참여하게 되어있다.

 하지만 특정한 이유때문에 트랜잭션이 독립적으로 수행되어야 한다면 ?

 

 spring은 7가지 트랜잭션을 전파할 수 있는 유형이 있다.

 

  •  PROPAGATION_REQUIRED ( default)
    • 부모 트랜잭션이 존재할 경우 부모 트랜잭션에 참여한다.
    • 부모 트랜잭션이 없을 경우 새 트랜잭션을 시작한다.
  • PROPAGATION_SUPPORTS
    • 부모 트랜잭션이 존재할 경우 부모 트랜잭션 참여
    • 없을경우  트랜잭션이 없는것처럼 동작한다.
  • PROPAGATION_MANATORY
    • 부모 트랜잭션이 존재할 경우 부모 트랜잭션 참여
    • 없을 경우 예외 발생
  • PROPAGATION_REQUIRES_NEW
    • 부모 트랜잭션 유무에 상관없이 새 트랜잭션을 시작한다.
    • 부모 트랜잭션이 존재할 경우 부모 트랜잭션을 중지
  • PROPAGATION_NOT_SUPPORTED
    • 부모 트랜잭션 유무에 상관없이 트랜잭션 없이 동작
    • 부모 트랜잭션이 있을 경우 부모 트랜잭션을 중지한다.
  • PROPAGATION_NEVER
    • 항상 트랜잭션 없이 동작하며 상위에 부모 트랜잭션이 존재할 경우 예외 발생
  • PROPAGATION_NESTED
    • 부모 트랜잭션과 독립적으로 롤백되거나 커밋 될 수 있다.

4. 트랜잭션 격리수준

 

트랜잭션은 원자성 일관성 지속성을 보장한다. 문제는 격리성 이다. 

트랜잭션 간에 격리성을 완벽히 보장하려면 트랜잭션을 거의 차례대로 실행해야 한다. 

 

이렇게 하면 동시성 처리 성능이 매우 나빠진다. 이런 문제로 ANSI 표준은 트랜잭션의 격리 수준을 4단계로 나누어 정의했다.

 

  •  READ UNCOMMITED  ( dirty read , non reapeatable read , phantom read )
    • 커밋하지 않은 데이터를 읽을 수 있다. 
    • 트랜잭션 1 이 데이터를 수정하고 있는데 트랜잭션 2가 수정중인 데이터를 조회할 수 있다. ( dirty read )
  •  READ COMMITED  ( non reapeatable read , phantom read )
    • 커밋한 데이터만 읽을 수 있다.
    • 트랜잭션 1이 회원 A를 조회중인데 갑자기 트랜잭션 2가 회원 A를 수정하고 커밋하면 트랜잭션 1이 다시 A를 조회했을 때 수정된 데이터가 조회된다. ( non reapeatable read )
      • spring 애플리케이션에서 jpa 를 사용하면 영속성 컨텍스트가 non reapeatable read 를 방지해주기 때문에 기본적으로 read commited 환경에서도 non reapeatable read 를 방지할 수 있다.
  •  REPEATABLE READ ( phantom read )
    • 한 번 조회한 데이터를 반복해서 조회해도 같은 결과가 조회된다.
    • 트랜잭션 1이 10살 이하의 회원을 조회했는데 트랜잭션 2가 5살 회원을 추가하면 트랜잭션 1이 다시 10살 이하의 회원을 조회하면 하나가 추가된 상태로 조회된다 ( phantom read )
  •  SEROALIZABLE
    • 가장 엄격한 트랜잭션 격리수준 
    • phantom read 도 발생하지 않는다.

 

 

 

 

댓글