Transaction 이란?

Saru·2023년 3월 28일
0

봄의 Spring

목록 보기
3/3
post-thumbnail

DB를 다루는 여러 코드에서 Transaction은 필연적으로 사용 된다. 그럼 이 트랜잭션이 무엇이며 어떠한 특징을 갖고 있고, 어떻게 사용 되는지에 대해서 공부 해보려 한다.

Transation 의 정의

Transaction - 거래

단어의 뜻 그대로는 거래라는 뜻이다. 개발에서 보편적으로 사용되는 트랜잭션의 뜻은

트랜잭션(transaction)이란 "쪼갤 수 없는 업무 처리의 최소 단위"를 말한다. 거래내역이라고도 한다. 영어로 간략히 Tx라고 표기하기도 한다. 1초당 처리할 수 있는 트랜잭션의 개수를 TPS라고 한다.

쪼갤 수 없는 업무 처리의 최소 단위 가 가장 중요한 키워드이다.

보통 트랜잭션을 설명할때 은행업무에 빗대어 많이 표현하는데, 이용자가 누군가에게 송금을 했을 때, 계좌에서 돈이 빠져나가고, 상대방에게 정상적으로 전달되어 계좌의 잔액이 늘게되어 반영이 되기까지 어느 한곳에서도 따로 쪼개지지 않고 처리가 되어야 비로소 송금이 끝났다고 할 수 있을 것이다.

Transation 의 특징

트랜잭션을 구성하는 4가지의 특징이 있는데 흔히 ACID 라고 불리는 특징이다.

A - Atomicity(원자성)

일단 트랜잭션이 시작이되면 실행되는 행위들이 끝까지 모두 반영이 되던지, 예기치못한 상황에 의해서 중단 혹은 누락이 된다면 모두 시작전으로 반영이 되지 않아야 된다는 특징이 바로 원자성이다.

송금업무에서 받는사람의 잔고가 반영되는 부분에서 오류가 났다고 가정하자. 이때 원자성이 지켜지지 않는다면, 보내는 사람의 계좌는 보낼때마다 본인의 계좌에서 금액이 차감 될 것이다. 하지만 받는 사람의 계좌는 오류로 인해 잔고의 변화가 없을 것이다. 이대로 트랜잭션이 끝나게 된다면, 금액만 차감된 채로 끝나게 된다. 그래서 송금하기 전 계좌 금액으로 돌려주는 작업이 필요하다. 이 이러한 원자성을 지키기 위해 보통 DB에서는 RollBack 기능을 지원해주고 있고, 일이 완료 되었을때 끝을 저장해주는 Commit 기능을 지원해준다.

C - Consistency: 일관성

트랜잭션을 실행하기 전, 실행한 이후의 해당 데이터가 가지고 있었던 제약, 성질, 특징등이 바뀌면 안되고 그 전의 상태가 이전과 같아야 한다는 특징이 일관성이다.

내가 가지고 있던 계좌가 송금만 가능했던 계좌라고 가정했을때, 어떠한 주식거래를 하는 계좌로 송금만을 했다고 해서, 내 계좌가 주식가능 한 계좌로 바뀌지는 않을 것이다. 계좌에 걸려있는 송금만 가능한 제약 조건이 트랜잭션을 실행했다고 해서 바뀌지않고, 송금만 가능한 특징을 유지해야 하는 것이 일관성의 예시라고 볼 수 있다.

I - Isolation: 독립성(고립성)

각각의 트랜잭션은, 실행시에 다른 트랜잭션에 영향을 받지 않고, 독립되어야 하는 특징이다.

만약 나의 계좌에 1만원이 있을 때, A와 B에게 동시에 6천원씩 송금을 한다고 가정하면, 독립성이 지켜지지 않는다면 동시에 금액이 빠져 나갈 수 있게 될 것이다. 그럼 은행에서 나중에 2천원을 갚도록 요구하던지, 마이너스 통장이 돼야 할 것이다. 이런것을 방지하기 위해 독립성이 유지 되어야 한다. 동시에 실행을 한다고 하면, 먼저 실행되는 쪽이 발생해야 하고, A에게 송금하는 트랜잭션이 먼저 실행된다면, B에게는 금액 부족 혹은 4000원만 송금이 실행 될 것이다. 이것은 A 에게 송금이후, B 에게 송금하는 보통의 절차와 같은 결과가 나와야 한다.

D - Durability: 영구성(지속성)

트랜잭션이 실행되고 나서 정상적으로 완료가 되었다면, 해당지점 이후에 장애나 오류가 발생했다고 하더라도, 완료 시점의 데이터는 복구하거나 기록으로 확인 할 수 있어야 한다는 특징이다.

은행 어플에서 A에게 송금을 완료했다고 다른 작업중에 오류가 발생하여 어플이 종료 되었다고 가정하다. 다시 해당 어플로 접속했을때, 송금 트랜잭션이 완료된 기록은 없어지지 않고 다시 확인 할 수 있을 것이다. 이것은 영구성이 잘 지켜져 있기 때문이다.

DB (SQL) 에서의 트랜잭션 구현

SELECT, INSERT, UPDATE, DELETE 등의 DML 을 이용하여 트랜잭션 로직을 구현한다.
송금에 필요한 금액을 SELECT 하고, 조건을 만족한다면 UPDATE 를 통해 보내는 사람, 받는 사람의 계좌의 금액을 바꿔준다. 이후 모든 작업이 원할 하게 끝났다면 COMMIT 을 해주어 트랜잭션을 완료해 줄 수 있다.

중간에 문제가 생겼다면, ROLLBACK 을 해주어서 잔고가 부분 UPDATE 된 것을 되돌려 트랜잭션 이전의 상태로 만들어 줄 수 있을 것이다.

Java 에서의 트랜잭션 구현

Java Spring 에서는 JDBC를 거쳐 DB와 데이터를 주고 받는다.
기본적으로 JDBC 커넥션에서는 AutoCommit 이 true 인 상태이다. 이것을 개발자가 제어하기 위해서는 커넥션에서 AutoCommit(false) 설정을 해주고 commit 과 rollback을 설정해줘서 수동으로 제어 해줄 수 있다.
보통 @Transactional 어노테이션을 설정해 줌으로써 트랜잭션을 컨트롤 할 수 있다.
하지만 이때, 2개 이상의 DB에서 진행 할 경우, 하나의 트랜잭션으로 묶어서 진행을 할 수가없다. 대신 ChainedTransactionManager 을 사용하여 교차적인 트랜잭션을 하나의 트랜잭션 처럼 엮어서 사용할 수 있는 것으로 알고있다.

Java에서의 트랜잭션에 대한 설명은 다음 블로그에 잘 기술되어 있어서 링크를 남긴다

https://steady-coding.tistory.com/610

propagation

어떤 트랜잭션 에서 다른 메서드를 호출 할 때, 그 메서드도 트랜잭션이라면 어떻게 동작할지에 대한 설정을 할 수 있다. 이를 propagation (전파) 라고 한다. 기본 기폴트 설정으로는 REQUIRED 로 Transactional interface에 정의 되어있고, 다른 설정 Propagation 에 enum 클래
로 정의 되어있음을 살펴 볼 수 있다.

출처 https://mangkyu.tistory.com/269 / 망나니개발자 님 블로그

기본 REQUIRED 는 트랜잭션을 필요로 하고 부모 트랜잭션이 있다면, 그 트랜잭션에 참여 하게되고 없다면 본인이 기준 트랜잭션이 된다.

REQUIRED.NEW 는 트랜잭션을 필요로 하고 부모 트랜잭션이 있다면, 그 트랜잭션을 보류 시키고 항상 새로운 트랙잭션을 생성한다.

NESTED 는 자식 트랜잭션을 하나 생성한다. 부모 트랜잭션의 영향은 받으나 다른 외부에 영햐을 주지 않는다.

SUPPORTS 부모 는 트랜잭션이 있다면 그 트랜잭션에 참여하고, 없으면 그냥 트랜잭션 없이 진행한다.

MANDATORY는 부모 트랜잭션에 참여하게 되고, 없을 시 예외를 발생하게 된다.

NOT_SUPPORTED 는 부모 트랜잭션이 있다면 보류시키고 트랜잭션이 없는 상태로 진행하게 된다.

NEVER의 경우 모든 트랜잭션 없이 진행하며 부모 트랜잭션이 존재할경우, 예외를 발생 키시게 된다.

스프링 공식 문서에는 다음과 같이 설명하고 있다.

Spring Isolation

다음은 DB 에서 Isolation level 에 관한 좋은 블로그 글이 있어서 링크를 남긴다.
https://velog.io/@lsb156/Transaction-Isolation-Level

출처



공부하고 찾아보고, 적용해 본 내용을 중심으로 정리합니다. 개인공부 및 기록용이 주목적이며 최대한 정확한 정보를 습득하려고 노력하지만 틀린내용 이 포함될 가능성이 농후합니다. 본문에 대한 지적, 조언 언제든 감사히 받겠습니다.

profile
우당탕탕 개발기

0개의 댓글