제 2절 트랜잭션

  • 트랜잭션 (Transaction) : 업무 처리를 위한 논리적인 작업단위.
    하나의 트랜잭션이 두 개 이상의 갱신 연산일 수 있다. 데이터를 일관성 있게 처리하려면 트랜잭션에 속한 두 개 이상의 갱신 연산을 동시에 실행할 수 있어야 하나, 이는 불가한 일이라
    여러 개의 갱신 연산이 하나의 작업 처럼 전부 처리되거나 아예 하나도 처리되지 않도록 (All or Nothing) 동시 실행을 구현한다.

1. 트랜잭션의 특징

트랜잭션의 주요 특징(ACID)

  • 원자성(Atomicity)
    • 트랜잭션은 더 이상 분해가 불가능한 업무 최소 단위이므로, 전부 처리되거나 아예 하나도 처리되지 않아야 한다.
  • 일관성(Consistency)
    • 일관된 상태의 데이터베이스에서 하나의 트랜잭션을 성공적으로 완료하고 나면, 그 데이터베이스는 여전히 일관된 상태여야 한다. 트랜잭션 실행 결과로 데이터베이스 상태가 모순되지 않아야 한다.
  • 격리성(Isolation)
    • 실행 중인 트랜잭션의 중간 결과를 다른 트랜잭션이 접근할 수 없다.
  • 영속성(Durability)
    • 트랜잭션이 일단 그 실행을 성공적으로 완료하면 그 결과는 데이터베이스에 영속적으로 저장된다.

2. 트랜잭션 격리성

  • 격리성은 일관성과 마찬가지로 Lock 을 강하게 오래 유지할수록 강화되고, Lock 을 최소화 할 수록 약화된다.
가. 낮은 단계의 격리성 수준에서 발생할 수 있는 현상들
  1. Dirty Read
    • 다른 트랜잭션에 의해 수정되었으나, 아직 커밋되지 않은 데이터를 읽는 것.
    • 변경 후 커밋되지 않은 값을 읽었는데, 변경한 트랜잭션이 롤백한다면 커밋되지 않은 데이터를 읽은 트랜잭션은 비일관된 상태에 놓이게 된다.
  2. Non-Repeatable Read
    • 한 트랜잭션 내에서 같은 쿼리를 두번 수행 했는데, 그 사이 다른 트랜잭션이 값을 수정 또는 삭제하는 바람에 두 쿼리 결과가 다르게 나타나는 현상.
      1. t1 시점에 123번 계좌의 잔고는 55,000 원 이었다고 가정. 1번 쿼리를 통해 계좌에 55,000원 남은 것을 확인하고 t4 시점에 10,000원을 인출하려는데
      2. 중간에 TX2 트랜잭션에 의해 잔고가 5,000 원으로 변경되었다.
      3. TX1 사용자는 잔고가 충분한 것을 확인하고 인출 시도했음에도 불구하고 잔고부족 메시지를 받게된다.
  3. Phantom Read
    • 한 트랜잭션 내에서 같은 쿼리를 두 번 수행했는데, 첫 번째 쿼리에서 없던 유령(Phantom)레코드가 두 번째 쿼리에서 나타나는 현상.
      1. TX1 트랜잭션이 지역별고객과 연령대별 고객을 연속해서 집계하는 도중에
      2. 새로운 고객이 TX2 트랜잭션에 의해 등록되었다
      3. 지역별 고객과 연령대별 고객 두 집계 테이블을 통해 총 고객수 조회 시 다른 결과값이 된다.
나. 트랜잭션 격리성 수준
  • ANSI/ISO 표준에서 정의한 4가지 트랜잭션 격리성 수준 (Transaction Isolation Level)
Read Uncommitted트랜잭션에서 처리 중인 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용한다.
Read Committed트랜잭션이 커밋된 데이터만 다른 트랜잭션이 읽도록 허용하여 Dirty Read를 방지한다.
커밋된 데이터만 읽더라도 Non-Repeatable Read, Phantom Read 현상을 막지는 못한다.
읽는 시점에 따라 결과가 다를 수 있기 때문이다.
( 한 트랜잭션 내에서 쿼리를 두번 수행 했는데 두 쿼리 사이에 다른 트랜잭션이 값을 변경/삭제 하거나 새로운 레코드를 삽입하는 경우 )
Repeatable Read트랜잭션 내에서 쿼리를 두번 이상 수행할 때, 첫 번째 쿼리에 있던 레코드가 사라지거나 값이 바뀌는 현상을 방지한다.
Phantom Read 현상을 막지는 못한다. 없던 레코드가 나타날 수는 있다.
Serializable Read트랜잭션 내에서 쿼리를 두 번 이상 수행할 때 첫 번째 쿼리에 있던 레코드가 사라지거나 값이 변경됨은 물론 새로운 레코드가 나타나지도 않는다.

  • 트랜잭션 격리성 수준은 ISO에서 정한 분류 기준일 뿐이며, 모든 DBMS가 4가지 레벨을 다 지원하지는 않는다.
  • Oracle : Read Committed , Serializable Read 만 지원한다. ( Repeatable Read 구현 = for update 구문 이용 )
  • 대부분 Read Committed 를 기본 트랜잭션 격리성 수준으로 채택하므로 Dirty Read 발생 하지 않으나, Non-Repeatable Read, Phantom Read 현상에 대해서는 주의해야 한다.
  • 현상 발생 방지를 위해 DBMS 제공 기능을 이용할 수 있지만, 많은 경우 개발자가 직접 구현해 주어야 하기 때문이다.
  • 다중 트랜잭션 환경에서 DBMS가 제공하는 기능을 이용하여 동시성 제어하려면 명시적으로 Set Transaction 명령을 수행하면 된다. 아래는 Serializable Read 로 상향조정 하는 예시다.
set transaction isolation level read serializable;
  • Repeatable Read, Seriaizable Read 로 격리성을 올리면 ISO에서 정한 기준을 만족해야 하며, 이를 구현하기 위해 DBMS에서는 Locking 매커니즘에 의존한다.
  • 이럴 경우 동시성이 문제가 된다.
    대량의 데이터를 읽어 처리할 때 동시성이 심하게 나빠진다. 완벽한 데이터 일관성 유지를 위해 테이블 레벨 Lock 을 걸어야 할 때도 있다.
  • 대안으로 다중버전 동시성 제어(Multiversion Concurrency Control)을 채택하는 DBMS가 늘고있다.
  • 스냅샷 격리성 수준(Snapshot Isolation Level)이라 불리는 방식이며, 현재 진행 중인 트랜잭션에 의해 변경된 데이터를 읽고자 할 때는 변경 이전 상태로 되돌린 버전을 읽는 것이다.
    확정되지 않은 값을 읽으려는 것이 아니므로 공유 Lock 을 설정하지 않아도 된다.
    읽는 세션과 변경하는 세션이 서로 간섭 현상을 일으키지 않는다.