트랜잭션은 데이터베이스에서 일관성을 유지하기 위해 중요한 개념입니다.
하지만 트랜잭션 처리 과정에서 발생할 수 있는 문제를 방지하려면 트랜잭션 격리 수준을 이해하고 적절히 설정해야 합니다.
이 글에서는 트랜잭션 격리 수준의 개념과 실무에서 발생하는 문제, 그리고 그 해결법을 구체적으로 설명합니다.
1. 트랜잭션 격리 수준이란?
트랜잭션 격리 수준은 여러 트랜잭션이 동시에 수행될 때 데이터의 일관성을 유지하기 위한 기준을 정의합니다.
SQL 표준은 네 가지 격리 수준을 정의하며, 격리 수준이 높아질수록 데이터의 일관성은 높아지지만 성능은 저하될 수 있습니다.
격리 수준별 특성
격리 수준 | 현상 | 일관성 | 성능 |
---|---|---|---|
READ UNCOMMITTED | Dirty Read 허용 | 낮음 | 높음 |
READ COMMITTED | Dirty Read 방지 | 중간 | 중간 |
REPEATABLE READ | Non-Repeatable Read 방지 | 높음 | 낮음 |
SERIALIZABLE | Phantom Read 방지 | 최고 | 낮음 |
2. 격리 수준별 문제와 해결법
2.1 READ UNCOMMITTED
특성: 트랜잭션이 커밋되지 않은 데이터를 읽을 수 있습니다 (Dirty Read 허용).
-- 트랜잭션 A
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- 트랜잭션 B
SELECT balance FROM accounts WHERE id = 1; -- Dirty Read 발생 가능
문제: 커밋되지 않은 데이터를 다른 트랜잭션이 읽으면 잘못된 결과를 반환할 수 있습니다.
해결법: READ UNCOMMITTED는 거의 사용하지 않으며, 최소 READ COMMITTED를 사용하는 것이 좋습니다.
2.2 READ COMMITTED
특성: 커밋된 데이터만 읽을 수 있습니다 (Dirty Read 방지).
-- 트랜잭션 A
UPDATE products SET stock = stock - 1 WHERE id = 101;
-- 트랜잭션 B
SELECT stock FROM products WHERE id = 101; -- 업데이트 완료 후 커밋된 데이터만 읽음
문제: Non-Repeatable Read가 발생할 수 있습니다. 같은 트랜잭션 내에서 동일한 데이터를 조회해도 결과가 달라질 수 있습니다.
-- 트랜잭션 A
SELECT stock FROM products WHERE id = 101; -- 결과: 10
UPDATE products SET stock = stock - 1 WHERE id = 101;
COMMIT;
-- 트랜잭션 B
SELECT stock FROM products WHERE id = 101; -- 결과: 9 (값 변경됨)
해결법: Non-Repeatable Read를 방지하려면 REPEATABLE READ를 사용합니다.
2.3 REPEATABLE READ
특성: 같은 트랜잭션 내에서 동일한 데이터를 반복 조회해도 결과가 동일합니다 (Non-Repeatable Read 방지).
-- 트랜잭션 A
SELECT balance FROM accounts WHERE id = 1; -- 결과: 100
UPDATE accounts SET balance = balance - 50 WHERE id = 1;
COMMIT;
-- 트랜잭션 B
SELECT balance FROM accounts WHERE id = 1; -- 결과: 100 (변경 내용 반영되지 않음)
문제: Phantom Read가 발생할 수 있습니다. 새로운 데이터가 삽입되면 결과가 달라질 수 있습니다.
-- 트랜잭션 A
SELECT * FROM orders WHERE status = 'pending'; -- 결과: 10건
-- 트랜잭션 B
INSERT INTO orders (id, status) VALUES (101, 'pending');
COMMIT;
-- 트랜잭션 A
SELECT * FROM orders WHERE status = 'pending'; -- 결과: 11건 (Phantom Read)
해결법: Phantom Read를 방지하려면 SERIALIZABLE을 사용합니다.
2.4 SERIALIZABLE
특성: 모든 트랜잭션이 순차적으로 실행되며, Phantom Read를 방지합니다.
-- 트랜잭션 A
SELECT COUNT(*) FROM orders WHERE status = 'pending'; -- 결과: 10건
-- 트랜잭션 B
INSERT INTO orders (id, status) VALUES (101, 'pending'); -- 트랜잭션 A가 끝날 때까지 대기
문제: 높은 격리 수준으로 인해 트랜잭션 대기 시간이 길어지고 데드락이 발생할 가능성이 높습니다.
해결법: 가능한 경우 다른 격리 수준을 사용하거나, 트랜잭션의 범위를 줄여 대기 시간을 단축합니다.
3. 실무에서의 트랜잭션 격리 수준 선택
- READ COMMITTED: 일반적인 애플리케이션에서 추천. Dirty Read를 방지하면서도 성능을 유지.
- REPEATABLE READ: 데이터 정합성이 중요한 환경에서 사용. 예: 은행 시스템.
- SERIALIZABLE: 데이터 충돌 가능성이 높은 경우에만 사용. 예: 금융 거래.
4. 트랜잭션 격리 수준 설정
MySQL
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
PostgreSQL
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
결론
트랜잭션 격리 수준을 이해하고 적절히 설정하면 데이터 정합성을 유지하면서 성능을 최적화할 수 있습니다.
실무에서 발생할 수 있는 문제와 해결법을 참고하여 적절한 격리 수준을 선택해 보세요.
'DB' 카테고리의 다른 글
데이터베이스 파티셔닝 전략 비교: MySQL vs PostgreSQL (0) | 2025.01.21 |
---|---|
[PostgreSQL] PostgreSQL JSONB를 활용한 복잡한 데이터 처리 (0) | 2025.01.20 |
[Oracle] ORA-04036 에러 해결 및 SGA와 PGA 메모리 설정 가이드 (5) | 2025.01.17 |
[Oracle] Oracle Text 대량 텍스트 색인화 (feat. 상품검색기능) (7) | 2024.06.07 |
Oracle DB 관리자 필수 가이드: 기본 쿼리로 데이터베이스 리소스 정보 확인하기 (3) | 2024.05.22 |