Transaction이란? 전파속성과 분산 트랜잭션 처리까지

이아름·2025년 8월 22일
0

이번 프로젝트에서 Transaction 관련 가이드를 듣는데 전파 옵션이 어쩌구.. Required, Requires_New.. XA, Non-XA 용어가 많이 들리는데 정말 아무것도 몰라서 답답함을 느꼈다.

그래서 처음부터 이론적으로 정리를 해보고 싶어서
주제로 가져왔다.

Transaction에 대한 개념부터 시작해서
전파 옵션이 뭐고 다중 DB, MSA에서 Transaction을 어떻게 처리하는가까지 이야기를 해보려고 한다.

Transaction

자 그럼 Transaction이란 것이 뭐냐.
벡엔드 개발라면 Transaction에 대해 많이 들어봤을 것이다.

Transaction이란 무엇인가

Transaction이란?
데이터 변경 작업들을 논리적 단위로 묶어서 전부 성공하거나(Commit), 전부 취소(Rollback) 하게 하는 메커니즘.

예시를 들어 다시 설명해보겠다.

A가 B에게 100원을 송금해야 한다고 치자.

A의 통장에서는 100원이 - 되야 하고
B의 통장에서는 100원이 + 되야 한다.

그런데 만약 A의 통장에서 돈이 나갔는데
B의 통장에 돈이 들어오지 않는다면 어떻게 될까?

아마 그 은행은 난리가 날 것이다.

이렇게 동시에 성공하거나 실패해야하는 로직을 트랜잭션으로 처리를 하는 것이다.

이후 설명하겠지만 이것이 트렌젝션의 특징 중 원시성(Atomic)에 해당한다.

Transaction의 특징 4가지

그렇다면 어떤 경우 트렌젝션을 사용하는가?
그 트렌젝션이 제공하는 성질 4가지를 소개하겠다.

ACID (트랜잭션의 4특징)

Atomicity(원자성):

All or Nothing.
Transaction으로 처리하는 로직은 모두 성공하거나 모두 실패해야 한다.

위의 송금예시를 보면

A의 계좌에서 돈을 - 하는데 성공했는데 B의 계좌에 돈을 +하는데 실패했다면
A의 계좌에서 돈을 -하는 행위를 rollback(원복) 시킨다.

Commit은 수정한 데이터(insert, update, delete)를 영구적으로 DB에 저장하는 것이다.
Rollback은 Commit하지 않은 수정사항을 트렌젝션을 시작하기 전상태로 되돌리는 명령어 이다.

Consistency(일관성):

무결성 규칙을 깨뜨리지 않는다.

만약 계좌의 잔액은 -가 되면 안된다는 규칙이 있다면 트렌젝션은 해당 규칙을 깨는 커밋을 할 수 없다.

Isolation(격리성):

여러 트랜잭션이 동시에 실행되도 서로 간섭 최소화하여 순차적으로 동작하는 것처럼 보인다.

만약 A의 잔고가 100원이고 A가 B에게 100원을 송금하는데
거의 동시에 C가 A의 통장에서 100원을 인출하려고 한다면

A가 B에게 100원을 송금한 후에 C가 인출을 시도하도록 처리를 한다.
(각 트렌젝션을 격리하지 않으면 통장잔고가 -가 될 수 있기 때문에)

Durability(영속성):

커밋되면 장애가 나도 결과가 남아야 한다.

만약 A가 B에게 100원 송금을 성공한 직후 서버에 장애가 발생한다면 어떻게 될까?

트렌젝션은 이미 커밋을 완료한 내용이면 서버가 다운되도 커밋 내용이 안전하게 저장된다.



물리 트렌젝션과 논리 트렌젝션

그럼 만약 여러개의 트랜젝션 처리가 필요한 경우에는 어떻게 하는 걸까?

트랜잭션이 이미 진행 중일 때, 다른 새로운 트랜젝션이 실행되는 경우 적용하는 규칙을 전파라고 한다.

한 트랜잭션 내에서 다른 메서드 호출 시, 새로운 트랜잭션을 시작할지 기존 트랜잭션을 따를지 결정하는 규칙이 전파 규칙이다.

전파 규칙에 대해 알기 위해서는 물리 트랜젝션과 논리 트랜젝션을 알아야한다.

물리 트랜잭션 (Physical Transaction)

물리 트랜젝션은 실제 데이터베이스에 적용되는 트랜잭션으로 커넥션을 통해 커밋/롤백하는 단위이다.

논리 트랜잭션 (Logical Transaction)

논리 트랜젝션은 애플리케이션(프레임워크) 관점에서 여러 메서드 호출을 한 덩어리로 다루는 단위이다.

쉽게 말해 프레임워크가 트랜젝션을 추상화하여 관리하는 것이 논리 트랜젝션이다.

논리 트랜잭션 ⊇ 물리 트랜잭션

만약 Transactional 메서드 A가 B를 호출하고 B가 C를 호출하는 프로그램이 있고 이것이 하나의 논리 트랜젝션으로 묶여 있다고 할 때

이 A, B, C는 하나의 물리 트랜젝션일 수도 있고 여러개의 물리 트랜젝션일 수도 있다.

논리 트랜젝션과 물리 트랜젝션 사이에는 아래와 같은 규칙이 있다.

-> 모든 논리 트렌젝션이 커밋되야 물리 트렌젝션을 커밋한다.
-> 하나의 논리 트렌젝션이라도 오류가 나면 물리 트렌젝션도 롤백한다.

트랜잭션 전파 옵션

하나의 논리 트랜젝션이 어떤 물리 트랜젝션으로 구성되는 가를 결정하는 것이 전파규칙(옵션)이다.

전파 규칙(옵션)에는 여러가지가 있는데
그 중에서 Required와 Requires_New를 가장 많이 사용한다.

REQUIRED:

가장 기본적으로 사용하는 옵션으로, 이미 트랜잭션이 있으면 합류하고 없으면 새로 시작하는 옵션이다.

Requried는 2개의 논리 트렌젝션을 묶어서 하나의 물리 트렌젝션으로 처리한다.

👉 같은 트랜잭션으로 묶이기 때문에 커밋과 롤백을 함께한다.

REQUIRES_NEW:

무조건 새로운 트랜잭션 시작한다. 기존 트랜잭션은 잠시 보류한다.

Requires_New는 2개의 논리 트랜젝션이 별도의 물리 트랜젝션을 가진다.

👉 주로 독립적인 작업 (예: 로그 기록, 메일 발송 등)을 위해 사용한다.

transactional 메서드 A에서 B 메서드를 호출할 때 A의 성공 여부와 상관없이 별도로 B를 실행하고 싶을때 Required_New를 사용한다.

주로 로그성 업무에서 많이 사용하는 것 같다.

<참고> 그 외에 다른 전파 옵션은 아래와 같다.
전파 옵션의미물리 트랜잭션 관점주 용도/주의점
REQUIRED (기본)있으면 합류, 없으면 새로 시작가능하면 같은 물리 트랜잭션 공유호출자/피호출자 같이 성공/실패
REQUIRES_NEW무조건 새로 시작, 기존은 일시 중단새 물리 트랜잭션 시작알림/로그처럼 독립 커밋이 필요한 작업
NESTED기존 트랜잭션 내 세이브포인트 생성같은 물리 트랜잭션 + Savepoint부분 롤백. DB/플랫폼 지원 필요
SUPPORTS있으면 합류, 없으면 비트랜잭션상황따라조회성 로직
NOT_SUPPORTED트랜잭션 일시 중단, 비트랜잭션으로 실행중단장시간 I/O, 외부 호출 등
MANDATORY반드시 기존 트랜잭션 필요, 없으면 예외합류정책 강제
NEVER트랜잭션이 있으면 예외금지특정 구간 보호


그렇다면 만약 여러개의 db를 하나의 트랜젝션에 연결해야 하는 경우에는 어떻게 처리할까?

분산 트랜젝션

분산 트랜젝션이란?
여러 데이터베이스나 서버에서 동시에 발생해야 하는 작업을 하나의 작업처럼 묶어서 처리하는 기술을 의미한다.

이렇게 여러 자원(데이터베이스)를 사용할 때 사용하는 트랜잭션을 전역 트랜잭션(Global Transaction)이라고 한다. 반댓말은 지역 트랜잭션(Local Transaction)이다.

전역 트랜잭션에서는 주로 XA 프로토콜을 사용한다.

XA는 뭐지? 설명해보겠다.

분산 트랜잭션의 구현 방식(XA 와 Non XA)

분산 트랜잭션의 구현방식에는 크게 XA와 Non-XA가 있다.

XA(eXtended Architecture, 확장형 아키텍처)

XA란 2PC (2 Phase commit)을 통한 분산 트랜잭션 처리를 위해 X-Open이라는 곳에서 명시한 표준 프로토콜이다.

그럼 2PC는 뭐냐..

2PC (Two-Phase Commit)

2PC (Two-Phase Commit)는 prepare -> commit 단계를 거쳐서
모든 DB가 커밋가능한지 확인한 후에 여러 리소스를 한번에 커밋하여 원자성을 지키는 방식이다.

이 방식은 원자성은 보장되지만 락이 걸리는 시간이 길어지면 성능이 저하될 수 있다.

레거시 환경에서 주로 사용했던 방식이다.

예를 들어 A가 B에게 100원을 이체한다고 했을 때

  1. A의 통장에서 -100원
  2. B의 통장 +100원

A의 통장에서 -100원 인출 가능한지 확인(prepare)하고
B의 통장에서 +100원 입금 가능한지 확인(prepare)한다.
만약 둘 다 가능한 경우 각각의 통장에 처리(Commit)하라는 명령을 내린다.

이 때 A 통장과 B 통장은 commit 명령이 올 때까지 다른 트랜잭션이 접근할 수 없도록 Lock인 상태로 대기한다.

Non-XA 트랜젝션

Non-XA는 XA 프로토콜을 사용하지 않고 각각의 DB가 독립적으로 트랜잭션을 수행하는 방식이다. MSA에서 많이 사용하는 방식이다.

이 방식은 성능이 빠르고 구현이 단순하지만 원자성이 보장되지 않을 수 있기 때문에 추가 작업이 필요할 수 있다.

Non-XA 방식에서 원자성을 보장하기 위해 사용하는 것이 Saga 패턴이다.

XA 패턴에서는 모든 DB 트랜잭션을 동시에 처리했지만
Saga 패턴에서는 DB 처리를 순차적으로 처리하여 Lock 설정을 하지 않는다.

그 대신 트랜잭션이 실패한다면 전체 트랜잭션을 Rollback 대신 commit 하려했던 로직의 반대 로직으로 커밋 전으로 데이터를 복구하여 최종적 일관성(Eventual Consistency)를 보장하는 방식이다.

이렇게 로직적으로 롤백하는 트랜잭션을 보상 트랜잭션이라고 한다.

예를 들어 A가 B에게 100원을 이체한다고 했을 때

  1. A의 통장에서 -100원
  2. B의 통장 +100원

Saga에서는 1과 2를 순차적으로 처리하는데
만약 1은 성공했는데 2가 실패한다면

업로드중..

A의 통장에 +100원을 하여 트랜잭션 시작 시의 상태로 복구한다.
이 때 +100원을 하는 트랜잭션이 보상 트랜잭션이다.

마치며

이번 포스트에서는 트랜잭션이란 무엇인가. 전파방식에 대해 그리고 XA와 Non-XA에 대해 알아봤다.

이번 포스트는 공부하고 싶은 것은 많은데
설명 순서를 정하는데 어려움이 많았다.

이것도 알아야될거 같고.. 저것도 봐야하는데 해서 이것저것 많이 넣었다가
조금 중구난방이 된 느낌이 있다.

그래도 트랜잭션에 대해 한발짝 다가간 것 같은 느낌이다.

참고자료

https://mangkyu.tistory.com/269
https://waspro.tistory.com/734
https://ksh-coding.tistory.com/143

profile
반갑습니다

0개의 댓글