■ 트랜잭션 읽기와 격리 수준 (Isolation Level) 완벽 정리
Dirty Read, Non-Repeatable Read, Phantom Read를 SQL로 직접 재현해보자
1. 트랜잭션과 격리 수준이란?
데이터베이스는 항상 동시에 여러 트랜잭션을 처리합니다.
이때 각각의 트랜잭션이 혼자 실행되는 것처럼 보이게 하기 위한 개념이 바로 격리성(Isolation) 입니다.
하지만 완벽한 격리는:
- 락 증가
- 성능 저하
- 데드락 위험 증가
▶ 그래서 DB는 정확성과 성능 사이의 타협안으로
격리 수준(Isolation Level) 을 제공합니다.
2. 트랜잭션 읽기(Read)의 의미
트랜잭션에서의 “읽기”는 단순한 SELECT가 아닙니다.
다른 트랜잭션이 변경 중인 데이터를
어디까지 허용해서 볼 것인가?
이를 단계별로 나눈 것이 격리 수준입니다.
3. 격리 수준이 없으면 발생하는 이상 현상
격리 수준은 아래 이상 현상(Anomaly) 을 막기 위해 존재합니다.
이상 현상설명
| Dirty Read | 커밋되지 않은 데이터를 읽음 |
| Non-Repeatable Read | 같은 행을 두 번 읽었는데 값이 바뀜 |
| Phantom Read | 조건 조회 결과 행의 개수가 달라짐 |


4. ANSI SQL 표준 격리 수준 4단계
아래로 갈수록 격리 수준은 높아지고 / 성능은 낮아집니다
격리 수준DirtyNon-RepeatablePhantom
| READ UNCOMMITTED | X | X | X |
| READ COMMITTED | O | X | X |
| REPEATABLE READ | O | O | X |
| SERIALIZABLE | O | O | O |
5. 실습 준비: 테스트 테이블
아래 SQL은 PostgreSQL / MySQL 공통입니다.
DROP TABLE IF EXISTS iso_demo;
CREATE TABLE iso_demo (
id INT PRIMARY KEY,
grp INT NOT NULL,
val INT NOT NULL
);
INSERT INTO iso_demo VALUES
(1, 10, 100),
(2, 10, 200),
(3, 20, 300);
※ 반드시 세션 2개(쿼리창 2개) 를 열어주세요.
6. Dirty Read 재현 (MySQL 전용)
※ PostgreSQL은 Dirty Read를 구조적으로 허용하지 않습니다.
세션 A
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
UPDATE iso_demo SET val = 999 WHERE id = 1;
-- COMMIT 하지 않음
세션 B
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT val FROM iso_demo WHERE id = 1; -- 999가 보이면 Dirty Read
COMMIT;
세션 A
ROLLBACK;
▶ 존재하지 않았던 값을 읽게 됨
7. Non-Repeatable Read 재현
세션 A
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT val FROM iso_demo WHERE id = 1; -- 100
세션 B
BEGIN;
UPDATE iso_demo SET val = 111 WHERE id = 1;
COMMIT;
세션 A
SELECT val FROM iso_demo WHERE id = 1; -- 111
COMMIT;
▶ 같은 행을 두 번 읽었는데 결과가 달라짐
8. Phantom Read 재현
세션 A
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
SELECT COUNT(*) FROM iso_demo WHERE grp = 10; -- 2
세션 B
BEGIN;
INSERT INTO iso_demo VALUES (4, 10, 999);
COMMIT;
세션 A
SELECT COUNT(*) FROM iso_demo WHERE grp = 10; -- 3
COMMIT;
▶ 조건에 맞는 행이 “갑자기 늘어남”
9. REPEATABLE READ에서는 어떻게 될까?
PostgreSQL
- 트랜잭션 시작 시 스냅샷 고정
- Non-Repeatable / Phantom 모두 조회상 발생하지 않음
- 대신 커밋 시 충돌 오류가 발생할 수 있음
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
MySQL(InnoDB)
- 기본 격리 수준
- MVCC + Gap Lock 조합
- 일반 SELECT와 SELECT ... FOR UPDATE의 동작이 다를 수 있음
10. Lost Update (실무에서 가장 자주 발생)
두 트랜잭션이 같은 값을 읽고 각각 UPDATE 하면
마지막 커밋이 이전 변경을 덮어쓰는 문제가 발생합니다.
해결 방법
- SELECT ... FOR UPDATE
- 버전 컬럼을 이용한 낙관적 락
11. 핵심 요약
격리 수준은
“정확성을 위해 성능을 얼마나 희생할 것인가”를
트랜잭션 단위로 결정하는 선택지다.
12. 상황별 추천 격리 수준
상황권장 격리 수준
| 일반 웹 서비스 | READ COMMITTED |
| 재고 / 포인트 | REPEATABLE READ |
| 정산 / 금융 | SERIALIZABLE |
| 통계 / 로그 | READ UNCOMMITTED |
반응형
'나의 개발 > 데이터베이스' 카테고리의 다른 글
| 관계형 데이터베이스(RDB)란 무엇인가? (0) | 2025.12.29 |
|---|---|
| 인덱스 클러스터링 팩터란? (0) | 2025.12.26 |
| 클러스터드 인덱스란? (3) | 2025.12.26 |
| DB 스키마와 ERD를 설계·확장·운영하려면 어떻게 해야 할까? (요약) (0) | 2025.12.20 |
| DB 스키마와 ERD를 설계·확장·운영하려면 어떻게 해야 할까? (NestJS + TypeORM) (0) | 2025.12.20 |
댓글