트랜잭션 격리 수준(Isolation level)은 동시에 실행되는 트랜잭션이 처리될 때 트랜잭션끼리의 간섭을 나타내는 것이다.

격리 수준

격리수준은 크게

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE 이 네 가지가 있으며, 위에서 아래로 갈 수록 점점 높은 격리 수준을 의미한다. 격리 수준은 높을 수록 데이터 일관성은 보장되지만 트레이드오프로 성능은 떨어지는 관계가 있다.

1. READ UNCOMMITTED

  • 가장 낮은 격리 수준
  • 커밋되지 않은 데이터도 읽을 수 있음
  • Dirty Read, Non-repeatable Read, Phantom Read 모두 발생 가능
  • 성능은 가장 좋지만 데이터 일관성 문제가 많음
  • 똑같은 select를 수행하면 똑같은 결과를 반환해야 한다는 repeatable read 반환하는 데이터는 불가능

Dirty Read 다른 트랜잭션이 아직 커밋하지 않은 데이터를 읽음

Non-Repeatable Read 같은 조건의 쿼리를 실행했을 때 이전에 없던 새로운 행이 나타남

Phantom Read 같은 조건의 쿼리를 실행했을 때 이전에 없던 새로운 행이 나타남

**2. READ COMMITTED **

  • (트랜잭션이 시작되기 전) 커밋된 데이터만 읽을 수 있음
  • Dirty Read는 방지하지만 Non-repeatable Read, Phantom Read는 발생 가능
  • 대부분의 DBMS에서 기본 격리 수준으로 사용
  • ORACLE

3. REPEATABLE READ

  • 트랜잭션 내에서 같은 데이터를 여러 번 읽어도 동일한 결과 보장
  • Dirty Read, Non-repeatable Read 방지
  • Phantom Read는 여전히 발생 가능
  • MySQL InnoDB의 기본 격리 수준

4. SERIALIZABLE

  • 가장 높은 격리 수준
  • 모든 이상 현상 방지
  • 트랜잭션들이 순차적으로 실행되는 것과 동일한 결과 보장
  • 성능이 가장 떨어지지만 완전한 데이터 일관성 제공

동시성 문제들

Dirty Read

  • 다른 트랜잭션이 아직 커밋하지 않은 데이터를 읽음
시간 | 트랜잭션 A                    | 트랜잭션 B
-----|------------------------------|----------------
T1   | BEGIN                        |
T2   | UPDATE 계좌 SET 잔액=1000    | 
T3   |                              | BEGIN
T4   |                              | SELECT 잔액 FROM 계좌 → 1000 읽음
T5   | ROLLBACK                     |
T6   |                              | (잘못된 데이터 1000으로 작업 진행)

Non-repeatable Read (반복 불가능한 읽기)

  • 같은 트랜잭션 내에서 같은 데이터를 여러 번 읽었을 때 다른 값이 나오는 현상
시간 | 트랜잭션 A                    | 트랜잭션 B
-----|------------------------------|----------------
T1   | BEGIN                        |
T2   | SELECT 잔액 FROM 계좌 → 500   |
T3   |                              | BEGIN
T4   |                              | UPDATE 계좌 SET 잔액=1000
T5   |                              | COMMIT
T6   | SELECT 잔액 FROM 계좌 → 1000  |
T7   | (같은 데이터인데 다른 값!)     |

위 예시와 같이 트랜잭션 A가 같은 데이터를 두 번 읽었는데 다른 값이 나왔다.

Phantom Read (팬텀 리드)

  • 같은 조건의 쿼리를 실행했을 때 이전에 없던 새로운 행이 나타나거나 기존 행이 사라지는 현상
시간 | 트랜잭션 A                           | 트랜잭션 B
-----|--------------------------------------|----------------
T1   | BEGIN                                |
T2   | SELECT * FROM 주문                   |
     | WHERE 날짜='2024-01-01' → 3건        |
T3   |                                      | BEGIN
T4   |                                      | INSERT INTO 주문 VALUES(...)
T5   |                                      | (날짜='2024-01-01')
T6   |                                      | COMMIT
T7   | SELECT * FROM 주문                   |
     | WHERE 날짜='2024-01-01' → 4건        |
T8   | (새로운 행이 나타남!)                |

위 예시와 같이 트랜잭션 A가 같은 조건으로 검색했는데 첫 번째는 3건, 두 번째는 4건이 나오게 됨.