데이터베이스Medium#95
트랜잭션의 ACID 속성에 대해 각각 설명해주세요.
#DB#ACID#트랜잭션#데이터베이스
힌트
원자성, 일관성, 격리성, 지속성의 영어 첫 글자입니다.
정답 및 해설
트랜잭션의 ACID 속성에 대해 각각 설명해주세요.
ACID는 데이터베이스 트랜잭션의 신뢰성을 보장하는 4가지 핵심 속성입니다. 트랜잭션이란 하나의 논리적 작업 단위로, 여러 데이터베이스 연산을 묶어 전부 성공하거나 전부 실패하도록 보장합니다. ACID 속성을 통해 데이터베이스는 하드웨어 오류, 동시 접근, 소프트웨어 버그 등의 상황에서도 데이터 무결성을 유지합니다.
트랜잭션이란?
-- 계좌 이체 예시: 두 작업이 하나의 트랜잭션으로 처리되어야 함
BEGIN TRANSACTION;
UPDATE accounts
SET balance = balance - 10000
WHERE account_id = 'A001'; -- A 계좌에서 출금
UPDATE accounts
SET balance = balance + 10000
WHERE account_id = 'B001'; -- B 계좌에 입금
COMMIT; -- 모두 성공하면 반영
-- 중간에 오류 발생 시
ROLLBACK; -- 모든 변경사항 취소
출금만 되고 입금이 안 되면 심각한 문제가 발생합니다. 트랜잭션과 ACID 속성이 이를 방지합니다.
A - 원자성 (Atomicity)
정의
트랜잭션 내의 모든 연산은 **전부 완료(Commit)되거나 전부 취소(Rollback)**되어야 합니다. 부분적으로 실행된 상태는 존재할 수 없습니다("All or Nothing").
예시
-- 원자성 보장 예시
BEGIN TRANSACTION;
UPDATE orders SET status = 'PROCESSING' WHERE order_id = 101;
INSERT INTO order_history (order_id, status, changed_at)
VALUES (101, 'PROCESSING', NOW());
UPDATE inventory SET stock = stock - 1 WHERE product_id = 'P001';
-- 재고가 0 미만이 되면?
-- → 오류 발생 → ROLLBACK → orders, order_history 변경도 모두 취소
-- → 데이터 일관성 유지
COMMIT;
구현 방법 (DBMS 내부)
Undo Log (롤백 로그):
- 트랜잭션 시작 시 변경 전 데이터를 Undo Log에 기록
- 롤백 필요 시 Undo Log를 사용하여 원래 상태로 복원
Redo Log (리두 로그):
- 커밋된 변경사항을 Redo Log에 기록
- 시스템 장애 후 복구 시 Redo Log로 변경사항 재적용
C - 일관성 (Consistency)
정의
트랜잭션 전과 후 모두 데이터베이스가 정의된 모든 **제약 조건(Constraint)**을 만족하는 유효한 상태여야 합니다.
예시
-- 일관성 제약 조건들
CREATE TABLE accounts (
account_id VARCHAR(20) PRIMARY KEY,
balance DECIMAL(15, 2) NOT NULL,
CONSTRAINT balance_positive CHECK (balance >= 0) -- 잔액은 0 이상
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10, 2) NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) -- 참조 무결성
);
-- 일관성 위반 시도 (거부됨)
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 50000 WHERE account_id = 'A001';
-- 잔액이 30000원인 계좌에서 50000원 출금 시도
-- → CHECK 제약 위반: balance = -20000 < 0
-- → 트랜잭션 자동 롤백
COMMIT;
일관성의 책임 범위
DBMS가 보장하는 일관성:
✅ PRIMARY KEY 중복 불가
✅ FOREIGN KEY 참조 무결성
✅ NOT NULL 제약
✅ CHECK 제약 조건
✅ UNIQUE 제약
애플리케이션이 보장해야 하는 일관성:
⚠️ 비즈니스 규칙 (예: 하루 이체 한도)
⚠️ 복잡한 데이터 관계
⚠️ 도메인 특화 규칙
I - 격리성 (Isolation)
정의
동시에 실행 중인 여러 트랜잭션이 서로의 중간 상태를 볼 수 없어야 합니다. 각 트랜잭션은 독립적으로 실행되는 것처럼 보여야 합니다.
격리성 부재 시 발생하는 문제들
-- 1. Dirty Read (커밋되지 않은 데이터 읽기)
T1: BEGIN;
T1: UPDATE accounts SET balance = 100000 WHERE id = 'A001';
-- T1이 아직 커밋하지 않은 상태에서
T2: SELECT balance FROM accounts WHERE id = 'A001'; -- 100000 읽음 (더티 리드!)
T1: ROLLBACK; -- T1 롤백 → T2가 읽은 데이터는 존재하지 않는 데이터
-- 2. Non-repeatable Read (반복 읽기 불일치)
T1: SELECT balance FROM accounts WHERE id = 'A001'; -- 50000 읽음
T2: UPDATE accounts SET balance = 80000 WHERE id = 'A001';
T2: COMMIT;
T1: SELECT balance FROM accounts WHERE id = 'A001'; -- 80000 읽음 (값이 달라짐!)
-- 3. Phantom Read (유령 데이터)
T1: SELECT COUNT(*) FROM orders WHERE amount > 10000; -- 5개
T2: INSERT INTO orders (amount) VALUES (15000);
T2: COMMIT;
T1: SELECT COUNT(*) FROM orders WHERE amount > 10000; -- 6개 (幽靈 행 등장!)
격리 수준 (Isolation Level)
-- 격리 수준 설정
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
| 격리 수준 | Dirty Read | Non-repeatable Read | Phantom Read | 성능 |
|---|---|---|---|---|
| READ UNCOMMITTED | 발생 가능 | 발생 가능 | 발생 가능 | 최고 |
| READ COMMITTED | 방지 | 발생 가능 | 발생 가능 | 높음 |
| REPEATABLE READ | 방지 | 방지 | 발생 가능 | 보통 |
| SERIALIZABLE | 방지 | 방지 | 방지 | 최저 |
대부분의 DBMS 기본값:
- MySQL InnoDB: REPEATABLE READ
- PostgreSQL: READ COMMITTED
- Oracle: READ COMMITTED
- SQL Server: READ COMMITTED
격리 구현 방법
1. Lock-based Concurrency Control
- 공유 잠금(Shared Lock): 읽기 연산, 여러 트랜잭션 동시 소유 가능
- 배타 잠금(Exclusive Lock): 쓰기 연산, 단 하나의 트랜잭션만 소유
2. MVCC (Multi-Version Concurrency Control)
- PostgreSQL, MySQL InnoDB에서 사용
- 데이터의 여러 버전을 유지하여 읽기와 쓰기 충돌 최소화
- 읽기 연산이 잠금 없이 수행 가능 → 높은 동시성
D - 지속성 (Durability)
정의
한 번 커밋(Commit)된 트랜잭션의 결과는 시스템 장애(정전, 크래시 등)가 발생해도 영구적으로 유지됩니다.
구현 방법
WAL (Write-Ahead Logging):
1. 트랜잭션 변경사항을 먼저 로그 파일(WAL)에 기록
2. 그 후 실제 데이터 파일에 적용
3. 장애 시: WAL을 사용하여 커밋된 트랜잭션 재적용(Redo)
단계:
COMMIT 명령 수신
↓
Redo Log(WAL)에 변경사항 기록 (디스크에 fsync)
↓
COMMIT 완료 응답 (이 시점에서 지속성 보장)
↓
(비동기로) 실제 데이터 파일에 반영
-- PostgreSQL의 지속성 관련 설정
-- synchronous_commit: 로그가 디스크에 기록될 때까지 대기 여부
-- off로 설정 시 성능 향상되지만 지속성 약해짐 (데이터 유실 가능)
SET synchronous_commit = ON; -- 기본값, 지속성 보장
실무에서의 ACID
분산 데이터베이스와 ACID
단일 DB: ACID 완전 지원 (상대적으로 쉬움)
분산 DB: ACID 보장이 어려워짐
CAP 정리:
- Consistency(일관성), Availability(가용성), Partition tolerance(분단 내성)
- 세 가지를 모두 완벽하게 달성 불가
- NoSQL (MongoDB, Cassandra 등)은 일부 ACID 속성 포기하고 가용성/성능 택함
BASE (NoSQL의 접근):
- Basically Available: 기본적으로 가용
- Soft state: 일시적으로 일관성 없는 상태 허용
- Eventually consistent: 결국에는 일관성 달성
트랜잭션 실무 팁
-- 긴 트랜잭션은 피해야 합니다
-- ❌ 나쁜 예: 트랜잭션 내에서 외부 API 호출
BEGIN;
UPDATE orders SET status = 'PROCESSING' WHERE id = 101;
-- 이 시점에서 orders 테이블에 잠금 유지!
-- 외부 API 호출 (수 초 소요 가능)
CALL external_payment_api();
UPDATE orders SET status = 'PAID' WHERE id = 101;
COMMIT;
-- → 잠금 대기 증가, 데드락 위험
-- ✅ 좋은 예: 외부 호출을 트랜잭션 밖으로
-- 1. 상태를 PENDING으로 저장
-- 2. 트랜잭션 종료
-- 3. 외부 API 호출
-- 4. 결과에 따라 새 트랜잭션으로 상태 업데이트
-- 저장점(Savepoint) 활용
BEGIN;
SAVEPOINT before_update;
UPDATE products SET stock = stock - 1 WHERE id = 'P001';
-- 문제 발생 시
ROLLBACK TO SAVEPOINT before_update;
-- 일부만 롤백, 전체 트랜잭션은 유지
COMMIT;
정리
| 속성 | 의미 | 보장하는 것 | 구현 기술 |
|---|---|---|---|
| Atomicity (원자성) | All or Nothing | 부분 실행 없음 | Undo Log, Rollback |
| Consistency (일관성) | 제약 조건 항상 만족 | 데이터 무결성 | 제약 조건, 트리거 |
| Isolation (격리성) | 트랜잭션 간 독립성 | 동시성 문제 방지 | Lock, MVCC |
| Durability (지속성) | 커밋 후 영구 보존 | 장애 후 복구 | WAL, Redo Log |
핵심: ACID는 데이터베이스가 신뢰할 수 있는 트랜잭션 처리를 위해 반드시 보장해야 하는 속성들입니다. 특히 계좌 이체, 주문 처리 등 데이터 무결성이 중요한 금융/커머스 시스템에서 필수적입니다.