🗄️
데이터베이스
Transaction
트랜잭션
논리적 작업 단위. ACID 속성. Commit/Rollback.
트랜잭션
논리적 작업 단위. ACID 속성. Commit/Rollback.
트랜잭션(Transaction)은 데이터베이스의 상태를 변화시키는 하나의 논리적 작업 단위입니다. 여러 개의 연산이 모두 성공하거나, 모두 실패해야 하는 "전부 아니면 전무(All or Nothing)" 특성을 가집니다. 은행 이체, 주문 처리 등 데이터 무결성이 중요한 작업에 필수입니다.
-- 계좌 이체 트랜잭션
BEGIN TRANSACTION;
-- 출금 계좌에서 차감
UPDATE accounts
SET balance = balance - 100000
WHERE account_id = 'A001' AND balance >= 100000;
-- 영향받은 행이 0이면 잔액 부족
-- 입금 계좌에 추가
UPDATE accounts
SET balance = balance + 100000
WHERE account_id = 'A002';
-- 이체 내역 기록
INSERT INTO transfer_log (from_account, to_account, amount, created_at)
VALUES ('A001', 'A002', 100000, NOW());
-- 모든 작업 성공 시 커밋
COMMIT;
-- 실패 시 롤백
-- ROLLBACK;
import psycopg2
from psycopg2 import sql
def transfer_money(from_account, to_account, amount):
conn = psycopg2.connect(database="bank")
try:
with conn:
with conn.cursor() as cur:
# 출금 계좌 확인 및 차감 (FOR UPDATE로 행 잠금)
cur.execute("""
SELECT balance FROM accounts
WHERE account_id = %s
FOR UPDATE
""", (from_account,))
balance = cur.fetchone()[0]
if balance < amount:
raise ValueError("잔액 부족")
# 출금
cur.execute("""
UPDATE accounts SET balance = balance - %s
WHERE account_id = %s
""", (amount, from_account))
# 입금
cur.execute("""
UPDATE accounts SET balance = balance + %s
WHERE account_id = %s
""", (amount, to_account))
# 로그 기록
cur.execute("""
INSERT INTO transfer_log (from_acc, to_acc, amount)
VALUES (%s, %s, %s)
""", (from_account, to_account, amount))
# with 블록 종료 시 자동 COMMIT
print("이체 완료")
except Exception as e:
# 예외 발생 시 자동 ROLLBACK
print(f"이체 실패: {e}")
raise
finally:
conn.close()
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('postgresql://localhost/bank')
Session = sessionmaker(bind=engine)
def process_order(user_id, product_id, quantity):
session = Session()
try:
# 재고 확인 및 감소
product = session.query(Product).filter_by(id=product_id).with_for_update().one()
if product.stock < quantity:
raise ValueError("재고 부족")
product.stock -= quantity
# 주문 생성
order = Order(
user_id=user_id,
product_id=product_id,
quantity=quantity,
total_price=product.price * quantity
)
session.add(order)
# 결제 처리
payment = Payment(order=order, status='completed')
session.add(payment)
session.commit()
return order.id
except Exception as e:
session.rollback()
raise
finally:
session.close()
-- 세션 격리 수준 설정 SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 트랜잭션별 설정 BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- PostgreSQL: 현재 격리 수준 확인 SHOW transaction_isolation; -- MySQL: 격리 수준 확인 SELECT @@transaction_isolation;
class OrderSaga:
def __init__(self):
self.compensations = []
def execute(self, order_data):
try:
# Step 1: 재고 예약
self.reserve_stock(order_data)
self.compensations.append(lambda: self.release_stock(order_data))
# Step 2: 결제 처리
self.process_payment(order_data)
self.compensations.append(lambda: self.refund_payment(order_data))
# Step 3: 배송 요청
self.request_shipping(order_data)
self.compensations.append(lambda: self.cancel_shipping(order_data))
# 모든 단계 성공
return {"status": "success"}
except Exception as e:
# 실패 시 보상 트랜잭션 역순 실행
for compensation in reversed(self.compensations):
try:
compensation()
except Exception as comp_error:
log.error(f"보상 실패: {comp_error}")
raise SagaFailedException(str(e))