트랜잭션 (Transaction)
Connection & Session
Connection
- 클라이언트와 데이터베이스 서버 간의 물리적인 네트워크 연결
Session
- Conection이 생성된 후 특정 클라이언트와 데이터베이스 서버 간의 논리적인 작업 영역
- Session은 Connection 위에서 동작하며, 하나의 Connection에 대해 하나의 Session이 생성됨
- Session은 SQL 쿼리 실행, 트랜잭션 관리 등을 함

트랜잭션이란?
- 트랜잭션(Transaction)은 데이터베이스에서 하나의 논리적 작업 단위를 의미
- 여러 작업을 하나의 그룹으로 묶어 실행하며, 이 모든 작업이 성공(Commit)하거나 실패(Rollback)해야 데이터의 무결성과 일관성을 유지할 수 있음
- 트랜잭션은 데이터베이스 시스템에서 ACID 성질을 보장
- 일반적으로 데이터베이스를 변경하는 INSERT 문, DELETE 문, UPDATE 문의 실행을 트랜잭션으로 관리함
- 세션마다 트랜잭션이 관리됨
트랜잭션의 주요 요소
Commit
- 트랜잭션을 종료하며 트랜잭션에서 수행된 모든 작업을 데이터베이스에 영구적으로 저장하는 작업
- 트랜잭션이 성공적으로 수행되었음을 선언
예시
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
COMMIT;
Rollback
- 트랙잭션을 종료하며 트랜잭션에서 수행된 모든 작업을 취소하고 원래 상태로 복구하는 작업
- 트랜잭션이 수행을 실패했음을 선언
예시
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
ROLLBACK;
Auto Commit
- Auto Commit이 활성화된 상태에서는 별도로 COMMIT 명령을 실행하지 않아도 SQL 문이 실행될 때마다 자동으로 Commit이 수행
- MySQL, PostgreSQL 등 많은 데이터베이스는 기본값으로 Auto Commit 모드가 활성화되어 있음
예시
SET autocommit = 0;
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
SET autocommit = 1;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
트랜잭션 특징 - ACID
원자성(Atomicity)
- 트랜잭션에 포함된 모든 작업이 모두 성공하거나 모두 실패해야 한다는 것을 보장
- 작업 도중 실패가 발생하면, 트랜잭션이 수행한 모든 변경 사항이 롤백
예시:
계좌 이체에서 돈을 보내는 작업과 받는 작업 중 하나라도 실패하면 전체 작업이 취소됨
일관성(Consistency)
- 트랜잭션이 완료된 후 데이터베이스는 항상 일관된 상태를 유지해야 함
- 데이터베이스의 제약 조건(무결성, 고유성 등)이 트랜잭션 전후에도 유지됨
예시:
은행 계좌의 총 잔액이 트랜잭션 전후로 동일해야 함
격리성(Isolation)
- 동시에 실행되는 트랜잭션이 서로의 작업에 영향을 미치지 않도록 보장
- 여러 트랜잭션들이 동시에 수행되어도 그 결과는 순차적으로 실행된 결과와 같아야함
예시:
A계좌에서 B계좌로 5,000원을 이체하는 트랜잭션을 수행하는 동안에는 다른 트랜잭션이 A계좌와 B계좌에 접근할 수 없어야 함
지속성(Durability)
- 트랜잭션이 성공적으로 완료되면 해당 작업 결과가 영구적으로 반영되어야 함
- 시스템 장애가 발생하더라도 트랜잭션 완료 내용은 데이터베이스에 보존됨
예시:
A계좌에서 B계좌로 5,000원을 이체하는 트랜잭션이 성공적으로 완료된 후에는 항상 이체가 완료된 상태를 유지해야함
은행 이체가 완료된 후 전원이 나가도 송금 내역은 남아 있어야 함
트랜잭션 격리 수준 (Transaction Isolation level)
- 트랜잭션 격리 수준은 동시에 실행되는 트랜잭션 간의 데이터 격리 정도
- 동시에 여러 트랜잭션이 처리될 때 특정 트랜잭션이 다른 트랜잭션에서 변경하거나 조회하는 데이터를 볼 수 있도록 허용할지 말지를 결정하는 것
- 격리 수준이 높을수록 데이터 일관성은 좋아지지만, 성능은 낮아질 수 있음
1. READ UNCOMMITTED
- 다른 트랜잭션이 커밋하지 않은 변경 내용도 읽을 수 있음
- 가장 낮은 격리 수준으로, 성능은 높지만 데이터 무결성 문제가 생길 가능성이 큼
- 쉽게 말해 완료되지 않은 트랜잭션의 작업 결과를 읽을 수 있는 레벨
한다.
- Dirty Read, Non-Repeatable Read, Phantom Read 현상이 발생한다.
2. READ COMMITTED
- 다른 트랜잭션이 커밋한 변경 내용만 읽을 수 있음
- 즉, 트랜잭션이 완료(Commit)되지 않은 상황에서 다른 트랜잭션이 변경한 데이터 SELECT가 가능하지 않는 레벨
- 트랜잭션이 동일한 데이터를 두 번 조회할 때, 그 사이에 다른 트랜잭션이 Commit하면 값이 바뀔 수 있음
- Non-repeatable read, Phantom read 현상이 발생
3. REPEATABLE READ
- 트랜잭션 내에서 동일한 데이터를 여러 번 읽더라도 항상 동일한 결과를 반환
- 하지만 다른 트랜잭션이 새로운 데이터를 삽입했을 때 현재 트랜잭션에서 새로운 데이터에 대한 조회가 가능함
- Phantom Read 현상이 발생
- MySQL InnoDB의 기본 트랜잭션 격리 수준(이때 Phantom Read는 발생하지 않음)
MySQL InnoDB에서는 MVCC(Multi-Version Concurrency Control)를 통해 읽기 일관성을 유지하고, Next-Key Lock(레코드와 인덱스 간 갭에 대한 잠금)을 통해 삽입으로 인한 데이터 변화까지 방지하기 때문에 Repetatable Read 격리수준이어도 팬텀리드 현상은 일어나지 않음
※ MVCC (Multi-Version Concurrency Control)
- MVCC는 데이터베이스에서 다중 버전 동시성 제어를 제공하는 기술로, 트랜잭션이 시작될 때 생성된 스냅샷(Snapshot)을 기반으로 데이터를 읽음으로써 트랜잭션 간의 읽기-쓰기 충돌을 방지함
- 데이터 변경 시 기존 데이터를 덮어쓰지 않고 새로운 버전을 생성하며, 읽기 작업은 스냅샷을 참조하여 일관된 데이터를 제공함
- 이를 통해 동시성을 높이고 읽기 작업 중 락 경합을 줄일 수 있음
※ Next-Key Lock
- Next-Key Lock은 MySQL InnoDB에서 레코드 잠금(Record Lock)과 갭 잠금(Gap Lock)을 결합한 잠금 메커니즘으로, 특정 레코드뿐만 아니라 인덱스 범위(갭)도 잠금으로 보호함
- 이를 통해 다른 트랜잭션이 동일한 인덱스 범위 내에서 데이터를 삽입하거나 수정하지 못하게 하며, 팬텀 리드(Phantom Read) 현상을 방지하고 데이터 일관성을 유지함
4. SERIALIZABLE
- 트랜잭션이 접근 중인 데이터에 대해 다른 트랜잭션의 모든 접근이 불가능함
- 트랜잭션이 접근 중인 데이터에 대해 다른 트랜잭션이 변경, 삭제하지 못할 뿐만 아니라 새로운 데이터 삽입도 불가능함
- 가장 높은 격리 수준으로, 트랜잭션이 순차적으로 실행되도록 보장
트랜잭션에서 발생 할 수 있는 문제
- 트랜잭션이 동시에 실행될 때, 격리 수준(Isolation Level)이 낮으면 여러 가지 데이터 일관성 문제가 발생할 수 있음
- 대표적인 문제들은 Dirty Read, Non-Repeatable Read, Phantom Read가 있음
Dirty Read
- Commit되지 않은 데이터를 다른 트랜잭션이 읽는 문제
- 트랜잭션 A가 아직 Commit 또는 Rollback하지 않은 데이터를 트랜잭션 B가 읽어버릴 수 있음
- 만약 트랜잭션 A가 Rollback하면, 트랜잭션 B는 존재하지 않는 데이터를 읽은 셈이 됨 (일관성 깨짐)
예시
BEGIN;
UPDATE users SET balance = 5000 WHERE id = 1;
SELECT balance FROM users WHERE id = 1;
ROLLBACK;
SELECT balance FROM users WHERE id = 1;
Non-Repeatable Read
- 같은 데이터를 두 번 조회했을 때 값이 다를 수 있는 문제
- 첫 번째 조회 이후 다른 트랜잭션이 해당 데이터를 변경 후 Commit하면, 다시 조회했을 때 값이 달라질 수 있음
예시
BEGIN;
SELECT balance FROM users WHERE id = 1;
BEGIN;
UPDATE users SET balance = 5000 WHERE id = 1;
COMMIT;
SELECT balance FROM users WHERE id = 1;
Phantom Read
- 같은 조건으로 조회했을 때, 추가된 데이터로 인해 결과가 달라지는 문제
- 다른 트랜잭션이 새로운 데이터를 INSERT하거나 DELETE하면, 동일한 SELECT 결과가 달라질 수 있음
예시
BEGIN;
SELECT * FROM users WHERE balance > 5000;
BEGIN;
INSERT INTO users (id, name, balance) VALUES (3, '새로운 사용자', 6000);
COMMIT;
SELECT * FROM users WHERE balance > 5000;
| 격리 수준 | Dirty Read (더티 리드) | Non-Repeatable Read (비반복 가능 읽기) | Phantom Read (팬텀 리드) |
|---|
| READ UNCOMMITTED | ✅ 발생 가능 | ✅ 발생 가능 | ✅ 발생 가능 |
| READ COMMITTED | ❌ 방지됨 | ✅ 발생 가능 | ✅ 발생 가능 |
| REPEATABLE READ | ❌ 방지됨 | ❌ 방지됨 | ✅ 발생 가능 |
| SERIALIZABLE | ❌ 방지됨 | ❌ 방지됨 | ❌ 방지됨 |