백엔드 작업을 하면서 트랜잭션을 설정한 적이 있는데 당시에는 데이터 무결성을 위한 작업이구나 하고 넘겨서 이번에 제대로 짚고 넘어가 볼까 합니다.
트랜잭션(Transaction)이란?
트랜잭션(transaction)이란 "쪼갤 수 없는 업무 처리의 최소 단위"를 말합니다. 뜻만 봐서는 잘 이해가 되지 않는데요. 예시를 하나 들어보겠습니다.
- 예시
여기 A가 B에게 10000원을 송금하려고 합니다.
A가 송금 버튼을 누르면 먼저
1. A의 계좌의 잔액이 10000원 이상인지 확인해야 할 것입니다.
2. 그리고 B의 계좌에 10000원 전송하겠죠
3. 그러면 A의 계좌 잔액은 10000원이 차감되고
4. B의 계좌 잔액은 10000원이 증가합니다.
이때 데이터베이스의 장애로 인해 4번 동작만 실행되지 않으면 어떻게 될까요? A의 계좌에서 10000원이 송금되고 잔액에서 차감되었지만 B의 계좌 잔액은 변하지 않아 10000원이 공중분해 되어버릴 것입니다.
이처럼 1~4의 동작은 모두 성공적으로 실행되거나 만약 하나의 과정이라도 정상적으로 실행되지 않으면 이전까지 과정 전부가 실행되지 않아야 합니다. 성공적으로 모든 동작이 완료됐을 때 결과가 반영되는 것을 commit이라고 하고 과정에서 오류가 났을 때 이전 동작들이 실행되지 않은 것처럼 되돌리는 것을 rollback이라고 합니다. 이와 같이 마치 하나의 작업처럼 움직여야 하는 1~4의 과정을 하나의 트랜잭션이라고 볼 수 있죠.
트랜잭션의 성질
예시와 같이 데이터의 무결성(Interity)을 보장하기 위해 DBMS의 트랜잭션이 가져야 할 특징이 있는데요. 각 특성의 첫 글자를 따서 ACID 특성이라고 합니다.
1) Atomicity(원자성)
- 트랜잭션 내의 모든 명령은 반드시 완벽히 수행되어야 합니다.(부분적으로 실행되거나 중단되지 않는 것을 보장)
- 어느 하나라도 오류가 발생하면 트랜잭션 전부가 취소되어야 합니다.
- 트랜잭션의 연산은 모두 반영되도록 Commit 되거나 전혀 반영되지 않도록 Rollback 되어야 합니다.
위의 예시에서 보았듯이 트랜잭션을 전부 반영되거나 전부 반영되지 않아야 합니다.
2) Consistency(일관성)
- 트랜잭션의 작업 처리 결과는 항상 일관성이 있어야 합니다.
예를 들어 어떠한 칼럼의 속성이 수정되었다면 trigger를 통해 일괄적으로 모든 데이터베이스에 적용되어야 합니다.
3) Isolation(독립성, 격리성)
- 트랜잭션 수행 시 다른 트랜잭션 연산에 끼어들지 못하도록 보장되어야 합니다.
- 수행 중인 트랜잭션은 완전히 완료될 때까지 다른 트랜잭션에서의 수행 결과를 참조할 수 없습니다.
- 독립성 예시
예를 들어 A와 B가 동시에 C에게 10000원을 송금한다고 가정해 봅시다.
순차적으로 트랜잭션이 진행될 경우 C의 잔액은 50000원이 되어야 하지만 동시에 진행 될 경우 A와 B에서 모두 C의 잔액이 30000원으로 조회되어 DB에 40000원으로 잔액을 올려야 한다는 요청이 2번 들어올 뿐입니다. 결과적으로 C는 20000원을 송금받았지만 잔액이 10000원만 늘어나는 경우가 생길 수 있습니다. 때문에 트랜잭션은 독립성을 요구합니다.
-독립성 VS 동시성-
하지만 모든 트랜잭션이 순차적으로 작동하기만 한다면 서로 독립적이겠지만 CPU는 DBMS보다 인풋 아웃풋 작업을 빈번히 수행하기 때문에 CPU의 응답 대기 시간이 길어져 비효율적 작동을 하게 됩니다. 때문에 트랜잭션의 독립성과 동시성의 성능을 지키기 위해 트랜잭션 설정이 중요하게 작용합니다.
ex)
@Transactional(propagation=Propagation.NESTED)
스프링 프레임워크의 NESTED 설정 같은 경우 이미 진행 중인 트랜잭션이 있으면 그 안에 새로운 트랜잭션을 만드는 설정입니다.
그림과 같이 트랜잭션 1이 진행 중인데 새로운 메서드 2가 시작되었다면 트랜잭션 1 내부에 메서드 2를 트랜잭션 2로 삽입합니다. 이렇게 중첩된 트랜잭션 2는 부모인 트랜잭션 1의 commit과 rollback에는 영향을 받지만 트랜잭션 2의 commit과 rollback에는 트랜잭션 1이 영향을 받지 않습니다.
4) Durability(영속성, 지속성)
- 성공적으로 완료된 트랜잭션의 결과는 시스템이 고장 나더라도 영구적으로 반영되어야 합니다.
트랜잭션이 성공적으로 수행되어 commit 되었다면 그 결과는 데이터베이스에 영원히 지속되어야 합니다. 이를 위해 모든 트랜잭션은 로그(log)로 남겨져 어떠한 장애에도 대비할 수 있어야 합니다.
트랜잭션 상태
또한 트랜잭션은 그 상태를 크게 5가지로 분류할 수 있습니다.
- 활동(Active) : 트랜잭션이 실행 중인 상태
- 실패(Failed) : 트랜잭션 실행에 오류가 발생하여 중단된 상태
- 철회(Aborted) : 트랜잭션이 비정상적으로 종료되어 Rollback 연산을 수행한 상태
- 부분 완료(Partially Committed) : 트랜잭션의 마지막 연산까지 실행했지만, Commit 연산이 실행되기 직전의 상태
- 완료(Committed) : 트랜잭션이 성공적으로 종료되어 Commit 연산을 실행한 후의 상태
결국 commit이나 rollback에 도달하는 것을 알 수 있습니다.
이상으로 트랜잭션에 대해 알아보았습니다. 개발 환경에 따라 여러 가지 트랜잭션을 원하는 옵션으로 설정할 수 있는 능력은 backend 개발에 있어서 중요한 능력인 것 같습니다.
** 틀린 내용이 있을 시 지적해 주시면 감사하겠습니다.
'개발 > DataBase' 카테고리의 다른 글
[Database] ORM(Object Relational Mapping) (feat. Typeorm) (2) | 2023.03.10 |
---|