트랜잭션 격리 수준은 여러 트랜잭션이 동시에 실행될 때 어느 정도까지 서로의 변경을 볼 수 있게 할지 정하는 기준입니다. 격리를 강하게 하면 데이터 일관성은 높아지지만 잠금 대기나 충돌이 늘어 동시성과 처리량이 낮아질 수 있어, 업무에서 허용할 수 있는 이상 현상과 성능을 함께 고려해야 합니다.
대표적인 이상 현상은 다음과 같습니다.
- Dirty Read: 다른 트랜잭션이 아직 커밋하지 않은 값을 읽는 현상
- Non-Repeatable Read: 같은 트랜잭션에서 같은 행을 다시 읽었을 때 값이 달라지는 현상
- Phantom Read: 같은 범위 조건으로 다시 조회했을 때 행이 추가되거나 사라지는 현상
| 격리 수준 | 특징 |
|---|---|
READ UNCOMMITTED | 커밋되지 않은 변경도 읽을 수 있어 Dirty Read가 발생할 수 있습니다. |
READ COMMITTED | 커밋된 데이터만 읽지만, 조회할 때마다 새 스냅샷을 사용하므로 Non-Repeatable Read와 Phantom Read가 발생할 수 있습니다. |
REPEATABLE READ | 같은 트랜잭션의 일반 조회는 동일한 스냅샷을 사용하여 반복 조회 결과를 안정적으로 유지합니다. SQL 표준상 Phantom Read가 가능하지만 구현체에 따라 동작이 다릅니다. |
SERIALIZABLE | 트랜잭션을 순차적으로 실행한 것과 같은 수준의 격리를 제공하지만 대기와 충돌이 가장 많습니다. |
| 격리 수준 | Dirty Read | Non-Repeatable Read | Phantom Read |
|---|---|---|---|
READ UNCOMMITTED | ✅ 허용 | ✅ 허용 | ✅ 허용 |
READ COMMITTED | ❌ 방지 | ✅ 허용 | ✅ 허용 |
REPEATABLE READ | ❌ 방지 | ❌ 방지 | ✅ 허용* |
SERIALIZABLE | ❌ 방지 | ❌ 방지 | ❌ 방지 |
MySQL InnoDB의 격리 레벨은 표준에서 정의한 격리 레벨을 사용하며 기본값은 REPEATABLE READ입니다. 일반 조회는 MVCC 스냅샷을 사용하고, 잠금 조회나 범위 수정에서는 Next-Key Lock을 사용해 팬텀을 방지할 수 있습니다.
Oracle에서 지원하는 Isolation Level은 REPEATABLE READ와 SERIALIZABLE입니다.
ORacle의 SERIALIZABLE는 Snapshot Isolation으로 동작합니다.
SQL Server는 표준 SQL에서 정의한 Isolation Level를 기반으로 정의합니다.
PostgreSQL는 표준 SQL에서 정의한 Isolation Level를 기반으로 정의합니다.
PostgreSQL에서는 REPEATABLE READ가 Snapshot Isolation으로 동작합니다.
Spring에서 격리 수준을 지정하지 않으면 어떤 값이 사용되나요?
@Transactional의 기본값은Isolation.DEFAULT이므로 데이터베이스의 기본 격리 수준을 사용합니다. MySQL InnoDB는 기본적으로REPEATABLE READ를 사용하지만 운영 DB 설정이 다를 수 있으므로 실제 값을 확인해야 합니다.
격리 수준을 높이면 Lost Update도 항상 방지되나요?
아닙니다. 애플리케이션이 값을 읽고 계산한 뒤 저장하는 Read-Modify-Write 과정에서는 격리 수준만으로 갱신 유실을 완전히 막지 못할 수 있습니다. 원자적 UPDATE, 낙관적 락의 버전 검사, 비관적 락 등 업무에 맞는 동시성 제어가 필요합니다.
MVCC는 격리성과 동시성을 어떻게 높이나요?
변경 전 버전을 보관하고 트랜잭션의 Read View에 맞는 데이터를 반환하여 일반 조회가 쓰기 잠금을 기다리지 않게 합니다. 읽기와 쓰기의 충돌은 줄지만 과거 버전 관리 비용이 있고, 잠금 조회와 쓰기 작업에는 여전히 잠금이 필요합니다.