송민준의 개발노트

Spring transactional 본문

웹/Spring Framework

Spring transactional

송민준 2020. 7. 15. 08:58

* 사용 시 주의사항

 @Transactional 을 클래스 또는 메소드 레벨에 명시하면 해당 메서드 호출 시 지정된 트랜잭션에 작동하게 된다. 단 조건이 있다. 해당 클래스의 Bean을 다른 클래스의 Bean에서 호출할 때만 @Transactional을 인지하고 작동하게 된다.  ( 같은 빈 내에서는 작동하지 않음), Spring은 내부적으로 AOP를 통해 어노테이션을 인지해서 프록시 생성을 하여 트랜잭션을 자동으로 관리하기 때문임.

 

1. 트랜잭션의 성질

 

1) 원자성(Atomicity)

 - 한 트랜잭션 내에 실행한 작업들은 하나로 간주한다. 즉 모두 성공 또는 실패

 

2) 일관성(Consistency)

 - 트랜잭션은 일관성 있는 데이터베이스 상태를 유지한다.

 

3) 격리성(isolation)

 - 동시에 실행되는 트랜잭션들이 서로영향을 미치지 않도록 격리해야한다.

 

4) 지속성(Durability)

 - 트랜잭션을 성공적으로 마치면 결과가 항상 저장 되어야 한다.

 

스프링에서는 어노테이션 @Transactional 을 선언해서 트랜잭션 처리를 지원한다.

클래스나 메서드 위에 Transactional이 추가되면 프록시 객체가 생성되고 정상 여부에 따라 Commit 또는 Rollback 한다.

 

 

 

2. 다수의 트랜잭션 경쟁시 발생할 수 있는 문제

 - 특정 트랜잭션이 처리중이고 아직 커밋되지 않았는데 다른 트랜잭션이 그 레코드에 접근한 경우 다음과 같은 문제가 발생할 수 있다.

 

1) Dirty Read

 - 트랜잭션 A가 어떤 값을 1에서 2로 변경하고 아직 커밋하지 않은 상황에서 트랜잭션 B가 같은 값을 읽는 경우 트랜잭션 B는 2가 조회된다.

 

- 트랜잭션 B가 2를 조회 한 후 A가 롤백된다면 결국 트랜잭션 B는 잘못된 값을 읽게 된 것이다. 트랜잭션이 완료되지 않은 상황에서 데이터에 접근을 허용할 경우 발생하는 데이터 불일치이다.

 

2) Non-Repeatable Read

 - 트랜잭션 A가 어떤 값 1을 읽었다. 이후 A는 같은 쿼리를 또 실행할 예정인데, 그 사이에 트랜잭션 B가 값 1을 2로 바꾸고 커밋해버리면 A가 같은 쿼리 두번을 날리는 사이 두 쿼리의 결과가 다르게 되어 버린다.

 

- Dirty Read 보단 발생 확률이 낮다.

 

3) Phantom Read

 - 트랜잭션 A가 어떤 조건을 사용하여 특정 범위 값들 1~4 를 읽었다.

이후 A는 같은 쿼리를 실행할 예정인데, 그 사이에 트랜잭션 B가 같은 테이블에 값 5~7을 추가해버리면 A가 같은 쿼리 두번을 날리는 사이 두 쿼리의 결과가 다르게 되어 버린다.

 

- 한 트랜잭션에서 일정 범위의 레코드를 두번 이상 읽을 때 발생하는 데이터 불일치이다.

 

3. 스프링 트랜잭션 속성

- 위에 나열된 문제들을 방지할 수 있는 속성이다.

 

1) isolation(격리 수준)

- 트랜잭션에서 일관성이 없는 데이터를 허용하도록 하는 수준을 말한다.

 

▶ DEFAULT

- 기본 격리 수준(기본설정, DB의 Islation Level을 따름)

 

▶ READ_UNCOMMITTED (level 0)

- 커밋되지 않는(트랜잭션 처리중인) 데이터에 대한 읽기를 허용

 

- 즉 어떤 사용자가 A라는 데이터로 변경하는 동안 다른 사용자는 B라는 아직 완료되지 않은 데이터를 읽을 수 있다.

 

- Dirty Read 발생 가능

 

▶ READ_COMMITTED (level 1)

- 트랜잭션이 커밋 된 확정 데이터만 읽기 허용

 

- 어떠한 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 해당 데이터에 접근할 수 없다.

 

- Dirty Read 방지

 

▶ REPEATABLE_READ (level 2)

- 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능하다.

 

- 선행 트랜잭션이 읽은 데이터는 트랜잭션이 종료될 때까지 후행 트랜잭션이 갱신하거나 삭제가 불가능 하기 때문에 같은 데이터를 두 번 쿼리했을 때 일관성 있는 결과를 리턴한다.

 

- Non-Repeatable Read 방지

 

▶ SERIALIZABLE (level 3)

- 데이터의 일관성 및 동시성을 위해 MVCC(Multi Version Concurrency Control)을 사용하지 않음

(MVCC는 다중 사용자 데이터베이스 성능을 위한 기술로 데이터 조회 시 LOCK을 사용하지 않고 데이터의 버전을 관리해 데이터의 일관성 및 동시성을 높이는 기술)

 

- 트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 shared lock이 걸리므로 다른 사용자는 그 영역에 해당하는 데이터에 대한 수정 및 입력이 불가능하다.

 

- Phantom Read 방지

 

* 격리 수준이 올라갈수록 성능 저하의 우려가 있다.

사용예
@Transactional(isolation=Isolation.DEFAULT)
public void something (int a) {
	..
}

2) propagation(전파 옵션)

 - 트랜잭션 동작 도중 다른 트랜잭션을 호출하는 상황에서 선택할 수 있는 옵션

 

▶ REQUIRED

 - 디폴트 속성, 부모 트랜잭션 내에서 실행하며 부모 트랜잭션이 없을 경우 새로운 트랜잭션을 생성한다.

 

SUPPORTS

 - 이미 시작된 트랜잭션이 있으면 참여하고 그렇지 않으면 트랜잭션 없이 진행하게 만든다.

 

▶ REQUIRES__NEW

 - 부모 트랜잭션을 무시하고 무조건 새로운 트랜잭션이 생성

 

▶ MANDATORY

 - REQUIRED와 비슷하게 이미 시작된 트랜잭션이 있으면 참여한다.

 - 반면에 트랜잭션이 시작된 것이 없으면 새로 시작하는 대신 예외를 발생시킴

 - 혼자서는 독립적으로 트랜잭션을 진행하면 안되는 경우에 사용함.

 

▶ REQUIRES_NEW

 - 항상 새로운 트랜잭션을 시작한다.

 - 이미 진행중인 트랜잭션이 있으면 트랜잭션을 잠시 보류함

 

▶ NOT_SUPPORTED

 - 트랜잭션을 사용하지 않게 한다.

 - 이미 진행 중인 트랜잭션이 있으면 보류시킨다.

 

▶ NEVER

 - 트랜잭션을 사용하지 않도록 강제한다.

 - 이미 사용 중인 트랜잭션도 존재하면 안된다 있다면 예외를 발생시킴

 

▶ NESTED

 - 이미 진행중인 트랜잭션이 있으면 중첩 트랜잭션을 시작한다.

 - 중첩 트랜잭션은 트랜잭션 안에 다시 트랜잭션을 만드는 것이다.

 - 독립적인 트랜잭션을 만드는 REQUIRES_NEW와는 다름

사용예
@Transactional(propagation = Propagation.REQUIRED)
public void something (int a) {
	..
}

전파옵션에 대해 예시를 들어가며 설명한 포스팅

http://wonwoo.ml/index.php/post/966

 

spring transaction 전파 - 머루의개발블로그

이번 시간에는 spring transaction 전파에 대해서 알아볼 예정이다. transaction 전파란 현재 transaction 에서 다른 transaction 으로 이동할 때를 이야기 한다. 예를들어 AccountService에 transaction이 걸려 있는데 �

wonwoo.ml

https://reiphiel.tistory.com/entry/understanding-of-spring-transaction-management-practice

 

Spring 트랜잭션 관리의 이해 - 실전편

Spring 트랜잭션 관리방법 Spring(스프링)에서 트랜잭션(Transaction)을 관리하는 방법은 크게 서로 대비되는 2가지 방법으로 나눌 수 있습니다. 프로그램에 의한(Programmatic) 트랜잭션 관리 첫번째로 알�

reiphiel.tistory.com

 

 

3) readOnly

 - 트랜잭션을 읽기 전용으로 설정할 수 있다.

 

 - 성능을 최적화하기 위해 사용할 수도 있고 특정 트랜잭션 작업 안에서 쓰기 작업이 일어나는 것을 의도적으로 방지하기 위해 사용할 수도 있다.

 

- 일부 트랜잭션 매니저의 경우 읽기전용 속성을 무시하고 쓰기 작업을 허용할 수도 있기 때문에 주의 해야한다.

 

 - 일반적으로 읽기 전용 트랜잭션이 시작된 이후 INSERT, UPDATE, DELETE 같은  쓰기 작업이 진행되려면 예외가 발생한다.

 

 - aop/tx 스키마로 트랜잭션 선언을 할 때는 이름 패턴을 이용해 읽기 전용 속성으로 만드는 경우가 많다.

   보통 get이나 find 같은 이름의 메소드를 모두 읽기전용으로 만들어 사용하면 편리하다.

 

사용예
@Transactional(readOnly = true)
public void something (int a) {
	..
}

 

4) 트랜잭션 롤백 예외(rollback-for, rollbackFor, rollbackForClassName)

 - 선언적 트랜잭션에서는 런타인 예외가 발생하면 롤백한다.

 

 - 반면에 예외가 전혀 발생하지 않거나 체크 예외가 발생하면 커밋한다.

   체크 예외를 커밋대상으로 삼은 이유는 체크 예외가 예외적인 상황에서 사용되기 보다는 리턴 값을 대신해서 비즈니스적인 의미를 담은 결과를 돌려주는 용도로 많이 사용되기 때문이다.

 

 - 스프링에서는 데이터 액세스 기술 예외는 런타임 예외로 전환돼서 던져지므로 런타임 예외만 롤백 대상으로 삼은 것이다.

 

 - 하지만 기본 동작 방식을 바꿀 수도 있다.

 ex) 체크 예외지만 롤백 대상으로 삼아야 하는 것이 있다면 XML의 rollback-for 어트리뷰트나 어노테이션의 rollbackFor 또는 rollbackForClassName 앨리먼트를 이용해서 예외를 지정하면 된다.

 

▶ rollbackFor 속성

 - 특정 예외가 발생 시 강제로 Rollback

사용예
@Transactional(rollbackFor = Exception.class)
public void something (int a) {
	..
}

 

▶ noRollbackFor 속성

 - 특정 예외의 발생 시 Rollback 처리되지 않음

사용예
@Transactional(noRollbackFor = Exception.class)
public void something (int a) {
	..
}

 

5) timeout 속성

 - 지정한 시간 내에 해당 메소드 수행이 완료되어 있지 않은 경우 rollback 수행. -1 일 경우 no timeout(Default)

사용예
@Transactional(timeout=10)
public void something (int a) {
	..
}

 

 

* 여러개 사용시

사용예
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class, timeout=10)
public void something (int a) {
	..
}

 

 

 

참고

https://goddaehee.tistory.com/167

 

[Spring] Transactional 정리 및 예제

[Spring] @Transactional 정리 및 예제 안녕하세요. 갓대희 입니다. 이번 포스팅은 [ 스프링 어노테이션 @Transactional  ] 입니다. : ) 들어가기 앞서...... SI를 할때, 현재 어느 쇼핑몰 운영을 맡으며 개..

goddaehee.tistory.com

https://jsonobject.tistory.com/467

 

Spring Boot, @Transactional 전파 레벨 정리

@Transactional 사용시 주의사항 @Transactional 을 클래스 또는 메써드 레벨에 명시하면 해당 메써드 호출시 지정된 트랜잭션이 작동하게 된다. 단, 조건이 있다. 해당 클래스의 Bean을 다른 클래스의 Bean

jsonobject.tistory.com