트랜잭션(Transaction)의 사전적인 의미는 거래를 뜻한다. 이것은 하나의 작업을 위해 더 이상 분할될 수 없는 논리적인 작업 단위를 의미한다. 즉, 한꺼번에 수행되어야 할 일련의 연산모음을 뜻한다.
왜 우리는 트랜잭션이란 개념을 알아야 하는 것일까? 다음과 같은 상황을 상상해보자.
밥을 같이 먹은 대학생 A, B는 더치페이를 하기 위해 미리 결제한 A에게 B가 돈을 송금하는 상황이다. B가 A에게 돈을 송금함으로써, B의 계좌에서 잔고가 차감되었는데, 중간에 예기치 못한 오류로 인해 A의 계좌에는 입금이 되지 않은 상황이 발생했다.
이와 같이 트랜잭션이 가장 많이 활용되는 상황으로, 계좌이체를 예시로 들 수 있다. 계좌이체는 입금과 출금 두 가지 과정으로 이루어진다. 만약 이처럼 B는 송금을 성공했지만, A의 계좌에서는 입금이 실패했다면 돈이 사라지는 결과를 볼 수 있다. 이런 이유로 두 과정이 원자적(Atomic)처럼 수행되도록 보장할 필요가 있다. 즉, 입금과 출금의 작업을 하나의 단위로 묶어, 둘 다 성공하거나 실패하도록 만들 수 있다. 이를 가능하게 하는 것이 트랜잭션의 개념이다.
트랜잭션은 이외에도 많은 상황에서 활용된다. 데이터 처리의 안정성을 확보하기 위한 필수적인 매커니즘으로, 특히 금융 거래와 같은 중요한 데이터를 처리할 때 이 개념은 더욱 중요한 역할을 한다.
이번 글에서는 트랜잭션의 개념과 함께 이를 제어하는 TCL(Transaction Control Language)를 이해해보고자 한다.
트랜잭션은 수행되는 일련의 작업 집합으로, 하나의 논리적 단위를 형성한다. (All or Nothing) 하나의 트랜잭션은 한 개 이상의 SQL 쿼리로 구성될 수 있으며, 이 쿼리들은 완전히 처리되거나 아예 한 개도 처리되지 않아야 한다. 중요한 점은 트랜잭션이 성공적으로 완료되면 그 결과가 영구적으로 데이터베이스에 반영되고, 실패할 경우에는 데이터베이스가 원래 상태로 돌아가야 한다.
트랜잭션은 다음과 같이 안전성을 보장하기 위해 네 가지 특성(ACID)을 가진다.
트랜잭션은 연산과정의 단계를 나타내면 아래와 같이 볼 수 있다.
트랜잭션은 독립적으로 처리되어야 하지만, 여러 트랜잭션이 동시에 실행될 때, 서로의 작업에 영향을 미칠 수 있다. 이러한 영향을 최소화하고 데이터의 일관성을 보장하기 위해, 트랜잭션 격리 수준(Isolation Level)이 필요하다. 격리 수준은 다른 트랜잭션의 중간 작업 결과를 얼마나 볼 수 있는지를 결정하는 기준이다.
트랜잭션이 허용하는 격리 수준에 따라 아래와 같이 네 가지로 구분할 수 있다.
이와 같은 격리 수준을 선택할 때는 시스템의 요구 사항과 성능 사이에서 균형을 고려해야 한다. 예를 들어, 금융 시스템과 같이 높은 일관성이 요구되는 경우, SERIALIZABLE 과 같이 높은 격리 수준을 선택할 수 있지만 그로 인한 성능 저하를 감수해야 한다. 반면에, 일관성보다 성능이 중요한 경우, READ COMMITTED와 같은 낮은 격리 수준이 적합할 수 있다.
이번에는 격리 수준에 따라 발생할 수 있는 현상에 대해 이해해보자.
| 구분 | Dirty Read | Non-Repeatable | Phantom Read |
|---|---|---|---|
| Read Uncommitted | 가능 | 가능 | 가능 |
| Read Committed | 불가능 | 가능 | 가능 |
| Repeatable Read | 불가능 | 불가능 | 가능 |
| Serializable Read | 불가능 | 불가능 | 불가능 |
트랜잭션을 제어하고 관리하기 위해 사용되는 SQL이다. 명령어는 주로 트랜잭션의 시작, 커밋, 롤백 등의 작업을 수행한다. 주요 TCL 명령어는 크게 세 가지(COMMIT, ROLLBACK, SAVEPOINT)가 존재한다.
하나의 트랜잭션을 정상적으로 완료하고 그 결과를 데이터베이스에 영구적으로 저장한다.
INSERT INTO EMP
VALUES (102, '홍길동');
COMMIT;
트랜잭션 중 실패하거나 오류가 발생한 경우, 이루어진 모든 변경 사항을 취소하고 데이터베이스를 트랜잭션 시작 전의 상태로 되돌린다.
UPDATE EMP
SET EMP_NAME = '이순신'
WHERE EMP_NAME = '홍길동';
ROLLBACK;
트랜잭션 내에서 특정 시점(SAVEPOINT)를 설정하여, 트랜잭션이 실패할 경우 이 지점으로 롤백할 수 있다. 트랜잭션 내에 여러 개의 SAVEPOINT를 지정할 수 있고, 일부만 롤백할 수 있도록 한다.
SAVEPOINT SP1;
INSERT INTO EMP VALUES(102, '홍길동');
SAVEPOINT SP2;
INSERT INTO EMP VALUES(103, '이순신;);
ROLLBACK TO SP2;
트랜잭션은 데이터베이스의 무결성과 일관성을 유지하는 데 필수적인 도구라고 생각된다. 이 개념이 존재하지 않는다면, 금융 시스템에서는 금전적인 문제와 직결되는 크나큰 문제가 발생할 수 있다. 따라서, 데이터를 다루는 시스템의 경우 안정성을 유지하기 위해 트랜잭션을 적절하게 사용하는 것이 좋을 것 같다.
앞으로 트랜잭션에 대한 개념을 더 깊게 살펴보도록 노력해야겠다.