트랜잭션 격리 수준이 존재하는 이유
여러 트랜잭션을 완벽하게 격리하면(서로 영향을 0으로) 가장 안전하지만 동시 처리량이 바닥난다. 반대로 격리를 풀면 빠르지만 데이터가 꼬인다. 격리 수준은 이 안전성과 성능 사이의 트레이드오프를 조절하는 장치다.
즉 격리 수준은 어떤 동시성 문제(이상 현상)를 허용하고 어떤 걸 막을 것인가를 정하는 것이라 할 수 있다. 격리 수준과 관련하여 3가지 타입의 이상 현상이 존재한다.
이상 현상
- Dirty Read (더티 리드) — 아직 커밋되지 않은 데이터를 읽는 것. 옆 트랜잭션이 값을 바꿔놨는데 그게 롤백되면, 나는 존재한 적도 없는 유령 값을 읽은 셈이 된다.
- Non-Repeatable Read (반복 불가능한 읽기) — 한 트랜잭션 안에서 같은 행(row)을 두 번 읽었는데 값이 달라지는 것. 내가 두 번 읽는 사이에 다른 트랜잭션이 그 행을 수정(UPDATE)하고 커밋했기 때문이다.
- Phantom Read (팬텀 리드) — 한 트랜잭션 안에서 같은 조건의 범위 쿼리를 두 번 실행했는데 결과 행의 개수가 달라지는 것. 그 사이 다른 트랜잭션이 조건에 맞는 새 행을 INSERT(또는 DELETE)했기 때문이다.
4단계 격리 수준
낮은 수준에서 높은 수준으로 갈수록 차단하는 이상 현상이 늘어난다.
| 격리 수준 | Dirty Read | Non-Repeatable | Phantom |
|---|---|---|---|
| Read Uncommitted | 발생 | 발생 | 발생 |
| Read Committed | 차단 | 발생 | 발생 |
| Repeatable Read | 차단 | 차단 | 발생 |
| Serializable | 차단 | 차단 | 차단 |
* 표준 정의 기준. MySQL InnoDB는 구현상 대부분 방지한다(아래 참고).
Read Uncommitted
커밋되지 않은 데이터까지 읽어 Dirty Read가 발생한다. 사실상 격리를 거의 하지 않는 수준이라 실무에서 거의 쓰지 않는다.
Read Committed
커밋된 데이터만 읽으므로 Dirty Read는 막는다. 하지만 한 트랜잭션 안에서 같은 행을 다시 읽을 때 그 사이 수정·커밋된 값을 읽으므로 Non-Repeatable Read가 발생할 수 있다. Oracle, PostgreSQL, SQL Server의 기본값이다.
Repeatable Read
한 트랜잭션이 시작된 시점의 데이터를 일관되게 보여줘, 같은 행을 몇 번 읽어도 동일한 값을 보장한다. 표준 정의상으로는 새 행이 INSERT되는 Phantom Read가 남는다. MySQL InnoDB의 기본값이다.
Serializable
가장 엄격한 수준으로, 모든 트랜잭션을 마치 한 줄로 세워 차례대로 실행한 것과 같은 결과를 보장한다. 세 이상 현상을 모두 막지만, 그만큼 잠금 범위가 넓어져 동시성이 크게 떨어진다.
격리 구현 방법
격리는 잠금 또는 MVCC로 구현
격리를 구현하는 방식은 크게 두 가지다. 잠금(Lock) 기반은 데이터를 잠가 다른 트랜잭션의 접근을 막는 것이고, MVCC(다중 버전 동시성 제어)는 데이터를 잠그는 대신 버전별 스냅샷을 따로 두어, 읽기와 쓰기가 서로를 막지 않게 하는 것이다.
Oracle, PostgreSQL, MySQL InnoDB 모두 MVCC를 활용해 높은 동시성을 확보한다.
'DATABASE' 카테고리의 다른 글
| 데이터베이스 동시성 — 비관적 락과 낙관적 락 (0) | 2026.06.26 |
|---|---|
| [ DATABASE ] - Oracle에서 사용자 생성 및 테이블스페이스 생성 (0) | 2025.01.07 |
| 데이터베이스 JOIN 종류 (0) | 2023.11.15 |
| [DATABASE] - 기본키·후보키·고유키·외래키와 인덱스 (0) | 2023.10.23 |
| [ mongodb ] - mongodb 비밀번호 까먹었을 때 (0) | 2022.12.04 |