트랜잭션
하나의 작업단위로 묶어 실행 시킨다.
트랜잭션은
모든 작업을 완료 (commit) / 모든 작업을 무효화 (rollBack)
단 두가지만 가능해야 한다.
원칙
트랜잭션에는 4가지 원칙이 있다.
원자성 (Atomicity)
트랜잭션은 원자적인 단위로 간주된다.
트랜잭션에 속한 모든 연산은 전체가 성공하거나 전체가 실패하는 것만 보장한다.
트랜잭션이 동작 중 어떤 연산이 실패하면 모든 상태가 이전으로 돌아간다(rollback).
일관성 (Consistency)
트랜잭션의 실행 전후에 데이터베이스는 일관된 상태여야 한다.
은행
A 계좌에서 돈을 인출 한다
B 계좌로 돈이 입금 된다 <- 예상한 행위가 이루어 져야 한다.
쇼핑몰
고객이 A상품을 구매한다
A상품은 재고가 차감 된다 <- 예상한 행위가 이루어 져야 한다.
고립성 (Isolation)
여러 트랜잭션이 동시에 실행될 때, 각 트랜잭션은 다른 트랜잭션의 영향을 받지 않아야 한다.
트랜잭션 동작시 다른 트랜잭션의 참조 , 변경 이 이루어지면 안된다.
고객 A의 주문 과정:
고객 A는 상품을 장바구니에 담고 주문을 시작합니다.
이 과정에서 데이터베이스의 상품 재고를 확인하고 잠금을 걸어둡니다.
그 후에 주문을 완료하고 상품의 재고를 감소시킵니다.
고객 B의 주문 과정:
고객 B도 동일한 상품을 구매하려고 합니다.
그러나 고객 A가 주문을 완료하기 전에 B가 주문을 시작합니다.
고객 B의 주문 과정도 상품 재고를 확인하고 잠금을 요청합니다.
하지만 이미 고객 A가 해당 상품에 대한 잠금을 가지고 있기 때문에 대기합니다.
고객 A의 주문이 완료되면 재고가 감소되고, 이후 고객 B의 주문이 처리됩니다.
고객 A와 고객 B의 주문 과정이 서로 영향을 미치지 않고 독립적으로 처리되어야 한다.
지속성 (Durability)
트랜잭션이 성공적으로 완료된 후에는 해당 변경 사항이 영구적으로 유지되어야 한다.
시스템의 장애 또는 비정상적인 종료가 발생하더라도 데이터베이스는 이전의 일관된 상태로 회복될 수 있어야 한다
로그기록 , DB 영속화
트랜잭션 전파 속성(Transaction Propagation)
트랜잭션은 작은 단위의 트랜잭션을 하나로 묶어 하나의 트랜잭션으로 수행할 수 있다.
다중 트랜잭션은 특히 분산 환경에서 여러 시스템 간의 트랜잭션 처리를 위해 사용된다.
전파 행동 (Propagation Behavior):
이 속성은 트랜잭션을 전달하는 방식을 결정합니다.
새로운 트랜잭션을 시작하고 이미 시작된 트랜잭션을 참여시키는 방법을 지정합니다.
전파 옵션 (Propagation Option):
이 속성은 트랜잭션 전파 동작 중에 발생할 수 있는 예외 상황에 대한 처리 방법을 결정합니다.
예를 들어, 트랜잭션 전파 중에 예외가 발생하면 해당 예외를 어떻게 처리할지를 지정합니다.
전파 속성 (Propagation Attribute):
이 속성은 트랜잭션 전파에 대한 추가적인 제어 옵션을 제공합니다.
예를 들어, 트랜잭션의 읽기 전용 여부, 트랜잭션의 격리 수준 등을 지정할 수 있습니다.
트랜잭션은 데이터베이스에서 제공하는 기술이므로 커넥션 객체를 통해 처리한다.
그래서 1개의 트랜잭션을 사용한다는 것은 하나의 커넥션 객체를 사용한다는 것이고, 실제 데이터베이스의 트랜잭션을 사용한다는 점에서 물리 트랜잭션이라고도 한다.
앞서 설명하였듯 트랜잭션 전파 속성에 따라서 외부 트랜잭션과 내부 트랜잭션이 동일한 트랜잭션으로 사용할 수 있다.
하지만 스프링의 입장에서는 트랜잭션 매니저를 통해 트랜잭션을 처리하는 곳이 2군데이다. 그래서 실제 데이터베이스 트랜잭션과 스프링이 처리하는 트랜잭션 영역을 구분하기 위해 스프링은 논리 트랜잭션이라는 개념을 추가하였다.
이 경우에는 2개의 트랜잭션 범위가 존재하기 때문에 개별 논리 트랜잭션으로 존재 하지만 실제로는 1개의 물리 트랜잭션이 사용된다.
만약 트랜잭션 전파 없이 1개의 트랜잭션만 사용되면 물리 트랜잭션만 존재하고, 트랜잭션 전파가 사용될 때 논리 트랜잭션 개념이 사용된다.
- 물리 트랜잭션: 실제 데이터베이스에 적용되는 트랜잭션으로, 커넥션을 통해 커밋/롤백하는 단위
- 논리 트랜잭션: 스프링이 트랜잭션 매니저를 통해 트랜잭션을 처리하는 단위
기존의 트랜잭션이 진행중일 때 또 다른 트랜잭션이 사용되면 복잡한 상황이 발생한다. 스프링은 논리 트랜잭션이라는 개념을 도입함으로써 상황에 대한 설명을 쉽게 만들고, 다음과 같은 단순한 원칙을 세울수 있었다.
- 모든 논리 트랜잭션이 커밋되어야 물리 트랜잭션이 커밋 됌
- 하나의 논리 트랜잭션이라도 롤백되면 물리 트랜잭션은 롤백 됌
[ REQUIRED 속성과 REQUIRES_NEW 속성 ]
스프링에는 7가지 전파 속성이 존재하는데, REQUIRED와 REQUIRES_NEW를 바탕으로 어떻게 진행되는지 살펴보도록 하자. REQUIRED와 REQUIRES_NEW를 이해하면 나머지는 응용이 가능하므로, 두 케이스만 자세히 살펴보도록 하자.
REQUIRED
REQUIRED는 스프링이 제공하는 기본적인(DEFAULT) 전파 속성으로,
기본적으로 N개의 논리 트랜잭션을 묶어 1개의 물리 트랜잭션을 사용하는 것이다.
REQUIRES_NEW
REQUIRES_NEW는 외부 트랜잭션과 내부 트랜잭션을 완전히 분리하는 전파 속성이다.
2개의 물리 트랜잭션이 사용되며, 각 각 트랜잭션 별로 커밋과 롤백이 수행된다.
이체 작업이 성공하면 로그를 데이터베이스에 기록하는 기능이 있다.
이때, 로그 작업이 실패하더라도 이체 작업은 롤백되어서는 안 되고, 고객의 계좌 이체는 완료되어야 한다.
[ 다양한 트랜잭션 전파 속성 ]
앞서 설명하였듯 스프링은 총 7가지 전파 속성을 제공한다. 각각에 대해 요약해서 정리하면 다음과 같다.
- REQUIRED
- SUPPORTS
- MANDATORY
- REQUIRES_NEW
- NOT_SUPPORTED
- NEVER
- NESTED
REQUIRED
- 의미: 트랜잭션이 필요함(없으면 새로 만듬)
- 기존 트랜잭션 없음: 새로운 트랜잭션을 생성함
- 기존 트랜잭션이 있음: 기존 트랜잭션에 참여함
REQUIRED는 디폴트 속성으로써 모든 트랜잭션 매니저가 지원하는 속성이다. 별도의 설정이 없다면 REQUIRED로 트랜잭션이 진행된다.
SUPPORTS
- 의미: 트랜잭션이 있으면 지원함(트랜잭션이 없어도 됨)
- 기존 트랜잭션 없음: 트랜잭션 없이 진행함
- 기존 트랜잭션이 있음: 기존 트랜잭션에 참여함
MANDATORY
- 의미: 트랜잭션이 의무임(트랜잭션이 반드시 필요함)
- 기존 트랜잭션 없음: IllegalTransactionStateException 예외 발생
- 기존 트랜잭션이 있음: 기존 트랜잭션에 참여함
REQUIRES_NEW
- 의미: 항상 새로운 트랜잭션이 필요함
- 기존 트랜잭션 없음: 새로운 트랜잭션을 생성함
- 기존 트랜잭션이 있음: 기존 트랜잭션을 보류시키고 새로운 트랜잭션을 생성함
NOT_SUPPORTED
- 의미: 트랜잭션을 지원하지 않음(트랜잭션 없이 진행함)
- 기존 트랜잭션 없음: 트랜잭션 없이 진행함
- 기존 트랜잭션이 있음: 기존 트랜잭션을 보류시키고 트랜잭션 없이 진행함
NEVER
- 의미: 트랜잭션을 사용하지 않음(기존 트랜잭션도 허용하지 않음)
- 기존 트랜잭션 없음: 트랜잭션 없이 진행
- 기존 트랜잭션이 있음: IllegalTransactionStateException 예외 발생
NESTED
- 의미: 중첩(자식) 트랜잭션을 생성함
- 기존 트랜잭션 없음: 새로운 트랜잭션을 생성함
- 기존 트랜잭션이 있음: 중첩 트랜잭션을 만듬
NESTED는 이미 진행중인 트랜잭션에 중첩(자식) 트랜잭션을 만드는 것으로, 독립적인 트랜잭션을 만드는 REQUIRES_NEW와 다르다. NESTED에 의한 중첩 트랜잭션은 부모 트랜잭션의 영향(커밋과 롤백)을 받지만, 중첩 트랜잭션이 외부에 영향을 주지는 않는다.
즉, 중첩 트랜잭션이 롤백 되어도 외부 트랜잭션은 커밋이 가능하지만 외부 트랜잭션이 롤백되면 중첩 트랜잭션은 함께 롤백되는 것이다. NESTED는 JDBC의 savepoint 기능을 사용하는데, DB 드라이버가 이를 지원하는지 확인이 필요하며 JPA에서 사용이 불가능하다.
위의 내용은 김영한님의 디비 접근 기술 2편 강의와 토비의 스프링 등을 바탕으로 정리한 내용입니다. 김영한님 강의를 듣지 않으셨다면 직접 들어보시는 것을 추천드립니다! 감사합니다 : )
출처: https://mangkyu.tistory.com/269 [MangKyu's Diary:티스토리]
'개-발 > Java + Spring + Kotlin' 카테고리의 다른 글
[MSA] Transaction Outbox 패턴 (0) | 2024.07.10 |
---|---|
[Spring] JDK Dynamic Proxy 와 CGLIB (0) | 2024.04.24 |
[Spring] Jasypt 중요 정보를 암호화 하자 (0) | 2024.04.12 |
[Spring] Fixture Monkey 테스트 라이브러리 (0) | 2024.04.06 |
[Spring Batch] 스프링 배치 개념 (0) | 2024.03.27 |