CS
트랜잭션과 격리 수준 완벽 가이드
민이(MInE)
2024. 11. 12. 15:08
반응형
목차
트랜잭션 기초
트랜잭션이란?
트랜잭션은 데이터베이스의 상태를 변화시키는 하나의 논리적 작업 단위입니다. 예를 들어, 계좌 이체는 출금과 입금이라는 두 개의 작업이 하나의 트랜잭션으로 처리되어야 합니다.
기본 예제
-- 계좌 이체 트랜잭션
START TRANSACTION;
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 'A';
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 'B';
COMMIT;
ACID 속성
1. Atomicity (원자성)
START TRANSACTION;
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 'A';
-- 여기서 에러 발생시 모든 변경사항 롤백
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 'B';
COMMIT;
2. Consistency (일관성)
-- 일관성 제약 조건 예시
CREATE TABLE accounts (
account_id VARCHAR(10) PRIMARY KEY,
balance DECIMAL(10,2) CHECK (balance >= 0)
);
3. Isolation (격리성)
-- 트랜잭션 격리 수준 설정
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
4. Durability (지속성)
-- MySQL 예제
SET innodb_flush_log_at_trx_commit = 1;
트랜잭션 격리 수준
1. READ UNCOMMITTED
가장 낮은 격리 수준으로, 다른 트랜잭션의 커밋되지 않은 변경사항을 읽을 수 있습니다.
-- 트랜잭션 1
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 'A';
-- 다른 트랜잭션의 커밋되지 않은 변경사항도 조회됨
2. READ COMMITTED
커밋된 데이터만 읽을 수 있습니다.
-- 트랜잭션 1
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 'A';
-- 다른 트랜잭션이 커밋한 데이터만 조회됨
3. REPEATABLE READ
트랜잭션 내에서 같은 쿼리를 실행하면 항상 같은 결과가 보장됩니다.
-- 트랜잭션 1
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 첫 번째 조회
SELECT balance FROM accounts WHERE account_id = 'A';
-- 다른 트랜잭션이 데이터를 변경하고 커밋해도
-- 두 번째 조회에서 같은 결과를 보장
SELECT balance FROM accounts WHERE account_id = 'A';
COMMIT;
4. SERIALIZABLE
가장 높은 격리 수준으로, 트랜잭션을 순차적으로 실행합니다.
-- 트랜잭션 1
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT * FROM accounts WHERE balance > 1000;
-- 다른 트랜잭션의 방해 없이 순차적 실행
COMMIT;
격리 수준에 따른 문제점
1. Dirty Read (더티 리드)
커밋되지 않은 데이터를 읽는 현상
-- 트랜잭션 1
START TRANSACTION;
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 'A';
-- 트랜잭션 2 (READ UNCOMMITTED에서 발생)
SELECT balance FROM accounts WHERE account_id = 'A';
-- 아직 커밋되지 않은 변경된 잔액을 읽게 됨
2. Non-Repeatable Read (반복 불가능한 읽기)
같은 쿼리를 두 번 실행했을 때 결과가 다른 현상
-- 트랜잭션 1
START TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 'A'; -- 잔액: 1000
-- 트랜잭션 2
UPDATE accounts SET balance = 2000 WHERE account_id = 'A';
COMMIT;
-- 트랜잭션 1
SELECT balance FROM accounts WHERE account_id = 'A'; -- 잔액: 2000 (READ COMMITTED에서 발생)
3. Phantom Read (팬텀 리드)
범위 쿼리를 실행할 때 다른 트랜잭션의 삽입/삭제로 인해 결과가 달라지는 현상
-- 트랜잭션 1
START TRANSACTION;
SELECT * FROM accounts WHERE balance BETWEEN 1000 AND 2000;
-- 트랜잭션 2
INSERT INTO accounts VALUES ('C', 1500);
COMMIT;
-- 트랜잭션 1
SELECT * FROM accounts WHERE balance BETWEEN 1000 AND 2000;
-- 새로운 레코드가 조회됨 (REPEATABLE READ에서도 발생 가능)
실전 예제
은행 계좌 이체 시스템
-- 격리 수준 설정
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 이체 트랜잭션
START TRANSACTION;
-- 잔액 검증
SELECT balance FROM accounts WHERE account_id = 'A' FOR UPDATE;
-- FOR UPDATE는 다른 트랜잭션의 수정을 막음
-- 출금
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 'A';
-- 입금
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 'B';
-- 잔액 재검증
SELECT balance FROM accounts WHERE account_id = 'A';
SELECT balance FROM accounts WHERE account_id = 'B';
COMMIT;
재고 관리 시스템
-- 동시성 제어를 위한 재고 관리
START TRANSACTION;
-- 재고 확인 (락 획득)
SELECT stock FROM inventory WHERE product_id = 100 FOR UPDATE;
-- 재고 감소
UPDATE inventory
SET stock = stock - 1
WHERE product_id = 100 AND stock > 0;
-- 주문 생성
INSERT INTO orders (product_id, quantity) VALUES (100, 1);
COMMIT;
성능과 격리 수준
격리 수준별 성능 비교
성능: READ UNCOMMITTED > READ COMMITTED > REPEATABLE READ > SERIALIZABLE
안정성: READ UNCOMMITTED < READ COMMITTED < REPEATABLE READ < SERIALIZABLE
성능 최적화 전략
-- 인덱스를 활용한 락 범위 최소화
CREATE INDEX idx_accounts_balance ON accounts(balance);
-- 트랜잭션 실행 시간 최소화
START TRANSACTION;
SELECT balance FROM accounts WHERE account_id = 'A' FOR UPDATE;
-- 빠른 처리를 위한 최소한의 작업만 수행
COMMIT;
모범 사례
1. 트랜잭션 시간 최소화
-- BAD
START TRANSACTION;
-- 긴 시간이 걸리는 외부 API 호출
UPDATE accounts SET balance = balance - 1000;
COMMIT;
-- GOOD
-- 외부 API 호출
START TRANSACTION;
UPDATE accounts SET balance = balance - 1000;
COMMIT;
2. 적절한 격리 수준 선택
-- 읽기 작업만 있는 경우
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 중요한 금전적 처리
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 매우 중요한 데이터 정합성이 필요한 경우
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
3. 데드락 방지
-- 데드락 방지를 위한 일관된 순서로 락 획득
START TRANSACTION;
SELECT * FROM accounts WHERE account_id IN ('A', 'B') ORDER BY account_id FOR UPDATE;
-- 항상 낮은 ID부터 락 획득
데이터베이스별 기본 격리 수준
MySQL (InnoDB)
-- 기본값: REPEATABLE READ
SHOW VARIABLES LIKE 'transaction_isolation';
-- 세션 격리 수준 변경
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
PostgreSQL
-- 기본값: READ COMMITTED
SHOW transaction_isolation;
-- 트랜잭션 격리 수준 변경
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
마치며
트랜잭션 격리 수준은 데이터베이스의 일관성과 성능 사이의 균형을 맞추는 중요한 요소입니다. 애플리케이션의 요구사항에 따라 적절한 격리 수준을 선택하고, 발생할 수 있는 문제점들을 이해하고 대비하는 것이 중요합니다.
핵심 포인트
- 트랜잭션의 ACID 속성 이해
- 각 격리 수준의 특징과 문제점 파악
- 성능과 데이터 일관성 사이의 균형
- 적절한 락 전략 수립
- 데드락 방지 방안 마련
반응형