DB 트랜잭션 완벽 가이드: ACID 속성으로 데이터 무결성 보장하기
데이터베이스 트랜잭션과 ACID 속성을 실전 예제로 배웁니다. 송금 시스템으로 원자성, 일관성, 격리성, 지속성을 이해합니다.
럿지 AI 팀
6분 읽기
목차
DB 트랜잭션 완벽 가이드
트랜잭션이란?
**정의:** 하나의 논리적 작업 단위
**핵심:** 모두 성공 or 모두 실패
실전 예제: 송금 시스템
시나리오
홍길동 → 김철수에게 10,000원 송금
필요한 작업
1. 홍길동 잔액 -10,000원
2. 김철수 잔액 +10,000원
문제 상황
``
sql
-- 1. 홍길동 잔액 차감
UPDATE accounts SET balance = balance - 10000 WHERE user_id = 1;
-- 성공!
-- 2. 여기서 시스템 다운!
-- 3. 김철수 잔액 증가 (실행 안 됨)
UPDATE accounts SET balance = balance + 10000 WHERE user_id = 2;
`
**결과:** 10,000원이 증발!
ACID 속성
A - 원자성 (Atomicity)
**의미:** 모두 성공 or 모두 실패
**트랜잭션 적용:**
`sql
START TRANSACTION;
UPDATE accounts SET balance = balance - 10000 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 10000 WHERE user_id = 2;
COMMIT; -- 모두 성공!
`
**오류 발생 시:**
`sql
START TRANSACTION;
UPDATE accounts SET balance = balance - 10000 WHERE user_id = 1;
-- 여기서 오류 발생!
UPDATE accounts SET balance = balance + 10000 WHERE user_id = 2;
ROLLBACK; -- 모두 취소!
`
C - 일관성 (Consistency)
**의미:** DB 규칙 항상 유지
**제약 조건:**
`sql
-- 잔액은 항상 0 이상
ALTER TABLE accounts
ADD CONSTRAINT check_balance CHECK (balance >= 0);
`
**테스트:**
`sql
START TRANSACTION;
-- 잔액 5,000원인데 10,000원 송금 시도
UPDATE accounts SET balance = balance - 10000 WHERE user_id = 1;
-- ERROR: Check constraint 위반!
ROLLBACK; -- 자동 롤백
`
I - 격리성 (Isolation)
**의미:** 동시 트랜잭션 간 간섭 없음
**문제 상황:**
`sql
-- 트랜잭션 1 (A가 B에게 송금)
START TRANSACTION;
UPDATE accounts SET balance = balance - 10000 WHERE user_id = 1;
-- 여기서 대기...
-- 트랜잭션 2 (C가 A의 잔액 조회)
SELECT balance FROM accounts WHERE user_id = 1;
-- 뭘 보여줘야 할까?
`
**격리 수준:**
`sql
-- READ COMMITTED (일반적)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- SERIALIZABLE (최고 수준)
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
`
D - 지속성 (Durability)
**의미:** COMMIT된 데이터는 영구 저장
**테스트:**
`sql
START TRANSACTION;
UPDATE accounts SET balance = balance + 10000 WHERE user_id = 1;
COMMIT;
-- 여기서 서버 다운해도...
-- 재시작 후 데이터 유지!
`
실전 예제
1. 송금 시스템
`sql
START TRANSACTION;
-- 1. 잔액 확인
SELECT balance FROM accounts WHERE user_id = 1 FOR UPDATE;
-- FOR UPDATE: 다른 트랜잭션의 수정 차단
-- 2. 잔액 차감
UPDATE accounts SET balance = balance - 10000
WHERE user_id = 1 AND balance >= 10000;
-- 3. 영향받은 행 확인
IF ROW_COUNT() = 0 THEN
ROLLBACK;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '잔액 부족';
END IF;
-- 4. 수취인 잔액 증가
UPDATE accounts SET balance = balance + 10000 WHERE user_id = 2;
-- 5. 거래 기록
INSERT INTO transactions (from_user, to_user, amount) VALUES (1, 2, 10000);
COMMIT;
`
2. 주문 처리
`sql
START TRANSACTION;
-- 1. 주문 생성
INSERT INTO orders (member_id, total_amount) VALUES (1, 50000);
SET @order_id = LAST_INSERT_ID();
-- 2. 주문 상세
INSERT INTO order_items (order_id, product_id, quantity, price)
VALUES (@order_id, 101, 2, 25000);
-- 3. 재고 차감
UPDATE products SET stock = stock - 2
WHERE product_id = 101 AND stock >= 2;
-- 4. 재고 부족 시 롤백
IF ROW_COUNT() = 0 THEN
ROLLBACK;
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '재고 부족';
END IF;
-- 5. 포인트 사용
UPDATE members SET points = points - 5000
WHERE member_id = 1 AND points >= 5000;
COMMIT;
`
격리 수준 비교
READ UNCOMMITTED
`sql
-- 트랜잭션 1
START TRANSACTION;
UPDATE accounts SET balance = 100000 WHERE user_id = 1;
-- COMMIT 전!
-- 트랜잭션 2
SELECT balance FROM accounts WHERE user_id = 1;
-- 100000 보임 (Dirty Read)
-- 트랜잭션 1
ROLLBACK; -- 취소!
-- 트랜잭션 2는 잘못된 값을 봤음!
`
READ COMMITTED (권장)
`sql
-- 트랜잭션 1
START TRANSACTION;
UPDATE accounts SET balance = 100000 WHERE user_id = 1;
-- 트랜잭션 2
SELECT balance FROM accounts WHERE user_id = 1;
-- 이전 값 보임 (Dirty Read 방지)
-- 트랜잭션 1
COMMIT;
-- 트랜잭션 2
SELECT balance FROM accounts WHERE user_id = 1;
-- 이제 100000 보임
`
REPEATABLE READ
`sql
-- 트랜잭션 1
START TRANSACTION;
SELECT balance FROM accounts WHERE user_id = 1; -- 50000
-- 트랜잭션 2
UPDATE accounts SET balance = 100000 WHERE user_id = 1;
COMMIT;
-- 트랜잭션 1
SELECT balance FROM accounts WHERE user_id = 1;
-- 여전히 50000 (반복 읽기 보장)
COMMIT;
`
교착 상태 (Deadlock)
문제
`sql
-- 트랜잭션 1
START TRANSACTION;
UPDATE accounts SET balance = balance - 1000 WHERE user_id = 1;
-- 트랜잭션 2를 기다림...
-- 트랜잭션 2
START TRANSACTION;
UPDATE accounts SET balance = balance - 1000 WHERE user_id = 2;
-- 트랜잭션 1을 기다림...
-- 둘 다 무한 대기! (Deadlock)
`
해결책
`sql
-- 항상 같은 순서로 잠금
START TRANSACTION;
-- user_id 작은 것부터
UPDATE accounts SET balance = balance - 1000
WHERE user_id = 1;
UPDATE accounts SET balance = balance - 1000
WHERE user_id = 2;
COMMIT;
`
오류 처리
자동 롤백
`sql
START TRANSACTION;
UPDATE accounts SET balance = balance - 10000 WHERE user_id = 1;
UPDATE accounts SET balance = balance + 10000 WHERE user_id = 999; -- 없는 사용자
-- 오류 발생 → 자동 ROLLBACK
`
명시적 롤백
`sql
START TRANSACTION;
-- 조건 확인
SELECT balance INTO @balance FROM accounts WHERE user_id = 1;
IF @balance < 10000 THEN
ROLLBACK;
ELSE
UPDATE accounts SET balance = balance - 10000 WHERE user_id = 1;
COMMIT;
END IF;
`
성능 vs 안전성
빠름 (위험)
`sql
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- Dirty Read 가능
`
느림 (안전)
`sql
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- 완벽한 격리, 느림
`
권장 (균형)
`sql
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 대부분의 경우 충분
`
핵심 포인트
1. 항상 트랜잭션 사용
**금융, 주문 등:**
데이터 무결성 필수
2. 짧게 유지
**나쁨:**
`sql
START TRANSACTION;
-- 복잡한 계산 (30초)
UPDATE ...;
COMMIT;
`
**좋음:**
`sql
-- 계산 먼저 (트랜잭션 밖)
-- 빠른 UPDATE (트랜잭션)
START TRANSACTION;
UPDATE ...;
COMMIT;
``3. 격리 수준 선택
**대부분:** READ COMMITTED
**특수:** SERIALIZABLE (중요 거래)
더 배우기
김영한의 실전 데이터베이스
- 트랜잭션 심화
- 실전 패턴
- 성능 최적화
---
**태그**: #트랜잭션 #ACID #데이터무결성 #DB안전성 #튜토리얼
L
럿지 AI 팀
AI 기술과 비즈니스 혁신을 선도하는 럿지 AI의 콘텐츠 팀입니다.
관련 포스트
비즈니스
탄탄한 DB 아키텍처로 시리즈 B 200억 투자 유치 성공
VC들이 가장 주목한 기술 경쟁력, 확장 가능한 DB 설계로 기업 가치 3배 인정받은 스타트업 사례입니다.
•3분 읽기
비즈니스
DB 설계로 스타트업 스케일링 성공: 유저 10배 증가 무장애 운영
처음부터 제대로 된 DB 설계로 유저 100만 돌파까지 리팩토링 없이 성공한 스타트업 사례입니다.
•4분 읽기
비즈니스
DB 최적화로 연간 서버 비용 12억 절감: 클라우드 비용 80% 감소
올바른 DB 설계와 최적화로 AWS 비용을 80% 절감하고 연간 12억을 아낀 중견 기업 사례입니다.
•4분 읽기