home

트랜잭션 격리 수준

  • 여러 트랜잭션이 동시에 처리될 때, 특정 트랜잭션이 다른 트랜잭션에서 변경한 데이터를 볼 수 있는지 여부를 결정
  • 차례로 격리 수준이 엄격함
    • Serializable
    • Repeatable Read
    • Read Committed
    • Read Uncommitted
  • 알고 가야하는 용어
    • Dirty Read: 커밋되지 않은 데이터를 읽는 현상
    • Non-Repeatable Read: 같은 쿼리를 반복 실행했을 때 결과가 달라지는 현상
    • Phantom Read: 같은 쿼리를 반복 실행했을 때 이전에 없던 레코드가 나타나는 현상

Serializable

  • 가장 엄격한 격리 수준
  • 트랜잭션이 동시에 실행되는 것을 막음
  • 각 트랜잭션이 순차적으로 실행된다.
  • 네가지 중에서 성능이 가장 떨어짐 + 교착상태에도 빠지기 쉬움
  • SELECT FOR UPDATE나 SHARE와 달리 순수 SELECT 작업도 레코드에 읽기 잠금을 한다.
  • Dirty Read, Non-Repeatable Read, Phantom Read 모두를 방지

Repeatable Read

  • 트랜잭션 시작 시점의 데이터 스냅샷을 읽는다.
  • MVCC(Multi-Version Concurrency Control)을 활용해서 한 트랜잭션 내에서 동일한 결과를 보장
    • DB는 변경 전, 후의 데이터를 모두 저장해두는데 MVCC를 통해서 롤백이 가능
    • 번호가 높은 트랜잭션이 커밋되었다고 해도, 번호가 낮은 트랜잭션보다 나중에 실행되었기 때문에 번호가 낮은 트랜잭션이 끝나지 않았다면 처음 트랜잭션 시작했을 때의 동일한 결과를 반환한다.
  • 하지만 레코드가 생성될 때는 부정합이 생성될 수도 있다.
    • Phantom Read가 발생할 수 있다는 건데 이는 SELECT FOR UPDATE/SHARE를 사용할 때 발생
    • 잠금이 있는 읽기는 언두 영역의 데이터가 아닌 테이블의 데이터를 읽게 되어서 문제 발생
      | id | name  | department |
      |----|-------|------------|
      | 1  | Alice | Sales      |
      | 2  | Bob   | Marketing  |
        
    
    • 이렇게 데이터가 있다고 했을 때, 트랜잭션 A는 다음과 같다고 가정하자.
      -- Time 1: 트랜잭션 A 시작
      START TRANSACTION;
        
      -- Time 2: Sales 부서의 직원 수를 조회
      SELECT COUNT(*) FROM employee WHERE department = 'Sales';
      -- 결과: 1
        
      -- Time 5: 다시 Sales 부서의 직원 수를 조회 (FOR UPDATE 사용)
      SELECT COUNT(*) FROM employee WHERE department = 'Sales' FOR UPDATE;
      -- 결과: 2 (Phantom Read 발생)
        
      -- Time 6: 트랜잭션 A 종료
      COMMIT;
        
    
    • 트랜잭션 B는 다음과 같다고 가정하자.
      -- Time 3: 트랜잭션 B 시작
      START TRANSACTION;
        
      -- Time 4: 새로운 Sales 직원 추가
      INSERT INTO employee (name, department) VALUES ('Charlie', 'Sales');
        
      -- Time 5: 트랜잭션 B 종료
      COMMIT;
        
    
    • Repeatable Read 격리 수준에서 일반적인 SELECT 쿼리는 트랜잭션 시작 시점의 스냅샷(언두)을 읽기 때문에 일관성이 유지가 된다.
    • 하지만 FOR UPDATE나 FOR SHARE와 같은 잠금이 있는 읽기 쿼리는 최신 데이터(테이블)의 데이터를 읽게 된다. 결과적으로, 같은 트랜잭션 내에서 실행된 두 쿼리가 서로 다른 결과를 반환하게 되어 Phantom Read가 발생
  • 하지만 Mysql은 SELECT FOR UPDATE를 걸어놨을 시, 갭락을 통해서 다른 트랜잭션이 조회 조건에 해당하는 새 레코드를 삽입하는 것을 방지한다.
  • Phantom Read가 발생하는 경우는 SELECT 이후의 트랜잭션에 SELECT FOR UPDATE를 걸어놨을 때 발생한다.

Read Committed

  • 다른 트랜잭션의 커밋된 변경사항만 읽을 수 있다.
  • 좋은 동시성을 제공하지만, 같은 쿼리를 반복실행할 때 결과가 달라질 수 있다.
    • 같은 트랜잭션 내에서 다른 트랜잭션이 UPDATE를 했을 때, 처음 SELECT 결과는 언두의 데이터를 반환하게 된다.
    • 하지만 다른 트랜잭션이 커밋을 하게 되면, 처음 SELECT 결과는 달리 최신 데이터를 반환하게 된다.
    • 정교한 트랜잭션이 필요한 경우에는 적합하지 않다.
  • Dirty Read는 방지되지만, Non-Repeatable ReadPhantom Read는 방지되지 않는다.

Read Uncommitted

  • 다른 트랜잭션의 커밋되지 않은 변경사항을 읽을 수 있다.
  • 최고의 동시성을 제공하지만 데이터 일관성이 매우 낮다.
  • 실시간 데이터 모니터링, 캐시 업데이트 등에 적합
  • Dirty Read, Non-Repeatable Read, Phantom Read 모두 발생 가능
    • 다른 트랜잭션의 작업이 완료되지도 않았는데 다른 트랜잭션에서 이를 읽게되면 데이터 부정합 문제가 발생 -> Dirty Read
  • 상당한 버그 발생 가능성이 있기 때문에 권장하지 않는다.