MySQL에서 성능과 데이터 정합성의 균형을 잡기 위한 핵심 기능 중 하나가 트랜잭션 격리 수준(Transaction Isolation Level)이다. 트랜잭션의 동시 실행 시 발생할 수 있는 다양한 문제들을 방지하기 위해 꼭 이해하고 넘어가야 할 개념이다. 이 글에서는 트랜잭션 격리 수준의 네 가지 단계인 READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE을 각각의 특징과 함께 정리하고, 실무에서 어떻게 활용해야 할지까지 살펴본다.
트랜잭션(transaction)이란 데이터베이스에서 하나의 작업 단위를 의미한다. 여러 쿼리를 하나의 묶음으로 실행하고, 모두 성공해야 커밋되고 하나라도 실패하면 롤백되는 특성을 가진다. 그런데 복수의 트랜잭션이 동시에 실행될 때 발생할 수 있는 대표적인 문제로는 Dirty Read, Non-repeatable Read, Phantom Read가 있다. 이 문제들을 얼마나 방지할 수 있느냐에 따라 격리 수준이 나뉜다.

격리 수준(Isolation Level)은 트랜잭션 간의 상호 작용을 얼마나 허용할 것인가에 대한 설정이며, 아래와 같이 총 4단계로 나뉜다.
✅ READ UNCOMMITTED (읽기 미확정)
가장 낮은 격리 수준이다. 다른 트랜잭션이 커밋하지 않은 데이터도 읽을 수 있는 위험한 모드로, 성능은 빠르지만 정합성이 매우 낮다.
예: 트랜잭션 A가 아직 커밋하지 않은 데이터를 트랜잭션 B가 읽고, 나중에 A가 롤백하면 B는 잘못된 데이터를 읽은 상태가 된다.
→ Dirty Read가 발생한다.
✅ READ COMMITTED (읽기 확정)
가장 널리 사용되는 수준이며, 다른 트랜잭션이 커밋한 데이터만 읽을 수 있다.
Dirty Read는 방지되지만, 같은 쿼리를 두 번 실행했을 때 결과가 다를 수 있다.
→ Non-repeatable Read가 발생한다.
✅ REPEATABLE READ (반복 가능 읽기)
MySQL InnoDB의 기본 설정이다. 트랜잭션이 시작된 시점의 데이터 스냅샷을 유지하여, 동일한 쿼리를 여러 번 실행해도 같은 결과를 보장한다.
→ Dirty Read, Non-repeatable Read는 방지되지만, Phantom Read는 여전히 발생할 수 있다.
⚠️ 단, MySQL의 InnoDB는 내부적으로 Gap Lock을 통해 Phantom Read도 막아준다.
✅ SERIALIZABLE (직렬화)
가장 엄격한 격리 수준으로, 모든 트랜잭션을 직렬(Serial)로 실행하는 효과를 낸다.
정합성은 최고 수준이지만 성능은 급격히 저하될 수 있다.
→ Dirty Read, Non-repeatable Read, Phantom Read 모두 방지된다.
📊 격리 수준별 현상 발생 비교
| 격리 수준 | Dirty Read | Non-repeatable Read | Phantom Read |
| READ UNCOMMITTED | 발생함 | 발생함 | 발생함 |
| READ COMMITTED | 방지됨 | 발생함 | 발생함 |
| REPEATABLE READ | 방지됨 | 방지됨 | (MySQL에서는 방지됨) |
| SERIALIZABLE | 방지됨 | 방지됨 | 방지됨 |
💡 실습 예제: 격리 수준 차이 체감하기
-- 세션 A
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 1;
-- 아직 COMMIT하지 않음
-- 세션 B (READ UNCOMMITTED일 때)
SELECT balance FROM account WHERE id = 1;
-- 수정 중인 balance를 볼 수 있음 (Dirty Read)
-- 세션 B (READ COMMITTED 이상일 때)
SELECT balance FROM account WHERE id = 1;
-- COMMIT 전까지는 수정 전 balance만 조회됨
-- 세션 A
START TRANSACTION;
SELECT * FROM orders WHERE user_id = 10;
-- 세션 B
INSERT INTO orders (user_id, item, quantity) VALUES (10, 'keyboard', 1);
COMMIT;
-- 세션 A (REPEATABLE READ이면 insert 결과가 반영되지 않음)
SELECT * FROM orders WHERE user_id = 10;
-- 같은 쿼리지만 INSERT된 row가 안 보임 (스냅샷 기반)
🔧 현재 MySQL 격리 수준 확인 및 변경 방법
-- 현재 세션의 격리 수준 확인
SELECT @@tx_isolation;
-- 전역 기본 격리 수준 설정 (my.cnf 또는 런타임)
SET GLOBAL transaction_isolation = 'READ COMMITTED';
-- 현재 세션에만 설정
SET SESSION transaction_isolation = 'REPEATABLE READ';
📌 실무에서 어떻게 활용할까?
- 대부분의 서비스에서는 READ COMMITTED를 선호한다. 이는 오라클, PostgreSQL에서도 기본값이며, Dirty Read를 막아 안정성을 확보하면서도 성능 손실을 최소화하기 때문이다.
- MySQL InnoDB는 기본이 REPEATABLE READ이므로, 원하지 않는 잠금 현상이나 성능 저하가 발생할 수 있다. 성능 튜닝이 필요하다면 READ COMMITTED로 낮춰보는 것도 고려할 수 있다.
- SERIALIZABLE은 통계 계산, 보고서 추출 등 데이터 정합성이 절대적으로 중요한 배치 처리에만 한정적으로 사용하는 것이 좋다.
📝 정리 포인트
- 트랜잭션 격리 수준은 성능과 정합성 사이의 균형을 맞추는 도구다.
- MySQL에서는 REPEATABLE READ가 기본이지만, 반드시 서비스 특성에 맞게 설정해야 한다.
- 실무에서는 실제 쿼리 실행 결과와 락 대기 상황까지 모니터링하면서 최적의 수준을 선택해야 한다.
📘 참고: 공식 가이드 문서
https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html
'DB' 카테고리의 다른 글
| [MySQL] (트랜잭션 격리수준5️⃣) 실무에서 격리 수준 설정은 이렇게 한다 (1) | 2025.07.16 |
|---|---|
| [MySQL] (트랜잭션 격리수준4️⃣) 격리 수준별 현상 실습: Dirty ~ Phantom Read (0) | 2025.07.16 |
| [MySQL] (트랜잭션 격리수준3️⃣) REPEATABLE READ vs SERIALIZABLE 차이 완전분석 (0) | 2025.07.16 |
| [MySQL] (트랜잭션 격리수준2️⃣) READ UNCOMMITTED vs READ COMMITTED (0) | 2025.07.15 |
| [MySQL] (환경설정6️⃣) 자주 실수하는 my.cnf 설정 오류 사례 (0) | 2025.07.15 |
| [MySQL] (환경설정5️⃣) my.cnf 실전 튜닝: 운영환경별 추천값 정리 (0) | 2025.07.15 |
| [MySQL] (환경설정4️⃣) query_cache는 아직 유효한가? 설정 전략 분석 (0) | 2025.07.15 |
| [MySQL] (환경설정3️⃣) 정렬 성능 향상: sort_buffer_size 설정법 (1) | 2025.07.14 |