트랜잭션(Transaction)

2024. 2. 21. 15:08

트랜잭션(Transaction) : DBMS에서 데이터를 다루는 논리적인 작업의 단위

- DB에서 데이터를 다룰 때 장애가 일어난 경우 데이터를 복구하는 작업의 단위가 된다.

- DB에서 여러 작업이 동시에 같은 데이터를 다룰 때가 이 작업을 서로 분리하는 단위

- 전체가 수행되거나 또는 전혀 수행되지 않아야 한다.

 

커밋 : 트랜잭션의 수행이 완료됨을 트랜잭션 관리자에게 알려주는 연산이다.

ACID 성질

1. 원자성(Atomicity) : 트랜잭션에 포함된 작업은 전부 수행되거나 전부 수행되지 않아야 한다.

2. 일관성(Consistency) : 트랜잭션을 수행하기 전이나 후에 데이터베이스는 항상 일관된 상태를 유지해야 한다.

3. 고립성(Isolation) : 수행 중인 트랜잭션에 다른 트랜잭션이 끼어들어 변경중인 데이터 값을 훼손하지 않아야 한다.

4. 지속성(Durability) : 수행을 성공적으로 완료한 트랜잭션은 변경한 데이터를 영구히 저장해야 한다.

 

 

트랜잭션과 DBMS

- DBMS는 원자성을 유지하기 위해 회복(복구) 관리자 프로그램을 작동시킴

- DBMS는 일관성을 유지하기 위해 동시성 제어 알고리즘과 무결성 제약조건을 활용함

- DBMS는 고립성을 유지하기 위해 동시성 제어 알고리즘을 작동시킴

- DBMS는 지속성을 유지하기 위해 회복 관리자 프로그램을 이용함.

 

 

 

 

 

트랜잭션의 5가지 상태

1. 활동(Active) : 트랜잭션이 Begin_transaction으로부터 실행을 시작하였거나 실행 중인 상태

2. 부분 완료(Partially committed) : 마지막 명령문을 실행시킨 직후의 상태. 실패 또는 완료의 상태로 전이하게 된다.

3. 실패(Failed) : 트랜잭션의 실행 중에 장애나 오류가 발생하여 정상적인 실행을 더 이상 할 수 없는 상태. Rollback 연산을 수행한 상태인 철회 상태로 전이하게 된다.

 

철회의 원인이 트랜잭션 자체의 논리적인 오류가 아닌 경우에는 재시작 되며, 트랜잭션 철회의 원인이 트랜잭션 내부의 논리적인 오류에 의해 오류를 수정해야 하는 상황이거나 얻고자 하는 데이터가 데이터베이스에 존재하지 않는 경우에 강제 종료된다.

 

 

2. 동시성 제어(Currency Control)

 - 다중 사용자 환경에서 둘 이상의 트랜잭션이 동시에 수행될 때, 일관성을 해치지 않도록 트랜잭션의 데이터 접근을 제어

 - 다중 사용자 환경을 지원하는 DBMS의 경우, 반드시 지원해야 하는 기능

  • 갱신 손실

 - 하나의 트랜잭션이 갱신한 내용을 다른 트랜잭션이 덮어씀으로써 갱신이 무효화 되는 것

 - 두 개의 트랜잭션이 한 개의 데이터를 동시에 갱신(Update)할 때 발생

 - 데이터베이스에서 절대 발생하면 안되는 현상

  • 모순성

 - 다른 트랜잭션들이 해당 항목 값을 갱신하는 동안 한 트랜잭션이 두 개의 항목 값 중 어떤 것은 갱신되기 전의 값을 읽고, 다른 것은 갱신된 후의 값을 읽게 되어 데이터의 불일치가 발생하는 현상.

  • 연쇄 복구

 - 두 트랜잭션이 동일한 데이터 내용을 접근할 때 발생

 - 한 트랜잭션이 데이터를 갱신한 다음 실패하여 Rollback 연산을 수행하는 과정에서 갱신과 Rollback 연산을 실행하고 있는 사이에 해당 데이터를 읽어서 사용할 때 발생할 수 있는 문제.

 

 

트랜잭션 스케쥴 : 연산들의 실행 순서

 

  • 직렬 스케쥴
    • 트랜잭션의 연산을 모두 순차적으로 실행하는 유형. 즉, 하나의 트랜잭션이 실행되면 완료되어야 다른 트랜잭션 실행 가능
  • 비직렬 스케쥴
    • 트랜잭션의 직렬 수행 순서와 상관없이 병행 수행하는 스케쥴
  • 직렬 가능 스케쥴
    • 서로 영향을 주지 않는 직렬 스케쥴을 비직렬적으로 수행
      • 두 개의 tx가 read 연산만을 수행할 것이라면, 상호 간섭 x, 연산의 순서도 중요치 않다.
      • 두 개의 tx이 같은 데이터 항목에 접근하지 않는다면 상호 간섭 x, 연산의 순서도 중요치 않다.
      • tx1이 x에 write 연산을 하고 tx2가 x에 read 또는 write 연산을 한다면 실행 순서는 중요하다.

 

격리성 수준

  • Read Uncommitted
    • 트랜잭션에서 처리 중인 아직 커밋되지 않은 데이터를 다른 트랜잭션이 읽는 것을 허용합니다. 
    • 해당 수준에서는 Dirty Read, Non-Repeatable Read, Phantom Read가 일어날 수 있다. 이 설정은 정합성에 문제가 있기 때문에 권장하지 않음
  • Read Committed
    • 트랜잭션이 커밋되어 확정된 데이터만 다른 트랜잭션이 읽도록 허용한다. 따라서 Dirty Read의 발생가능성을 막는다. 커밋되지 않은 데이터에 대해서는 실제 DB 데이터가 아닌 Undo 로그에 있는 이전 데이터를 가져오는 것.
    • Non-Repeatable Read, Phantom Read가 일어날 수 있다.
    • 반복 읽기를 수행하면 다른 트랜잭션의 커밋 여부에 따라 조회 결과가 달라질 수 있다. (금전 문제와 연결되면 문제 생길 수 있음. 계속 입금)
    • 애초에 커밋된 데이터만 읽을 수 있기 때문에 트랜잭션 내에서 실행되는 SELECT와 밖에서 실행되는 SELECT의 차이가 별로 없다.
  • Repeatable Read
    • 트랜잭션 내에서 삭제, 변경에 대해서 Undo 로그에 넣어두고 앞서 발생한 트랜잭션에 대해서는 실제 데이터가 아닌 Undo 로그에 있는 백업 데이터를 읽게 한다. 이렇게 함으로써 트랜잭션 중 값의 변경에 대해서 일정한 값으로 처리할 수 있다. 이렇게 하면 삭제와 수정에 대해서 트랜잭션 내에서 불일치를 가져오던 Non-Repeatable Read를 해소할 수 있다.
    • 변경 전의 레코드를 언두 공간에 백업해둔다. 그러면 변경 전/후 데이터가 모두 존재한다. = 동일한 레코드에 대해 여러 버전의 데이터가 존재한다고 하여 이를 MVCC라고 부른다.
    • 백업 레코드에는 어느 트랜잭션에 의해 백업되었는지 트랜잭션 번호를 함께 저장한다.
    • 한 트랜잭션 내에서 동일한 결과를 보장하지만, 새로운 레코드가 추가되는 경우 부정합이 생길 수 있다.
    • REPEATABLE READ는 트랜잭션 번호를 참고하여 자신보다 먼저 실행된 트랜잭션의 데이터만을 조회한다. 만약 테이블에 자신보다 이후에 실행된 트랜잭션의 데이터가 존재한다면 언두 로그를 참고해서 데이터를 조회한다.
    • SELECT로 조회한 경우 트랜잭션이 끝나기 전에 다른 트랜잭션에 의해 추가된 레코드가 발견될 수 있다 = 유령 읽기(Phantom Read). => 하지만 MVCC 덕분에 일반적인 조회에서 유령 읽기는 발생하지 않는다.
    • 유령 읽기가 발생하는 경우 = 잠금이 사용되는 경우
      • 일반적인 RDBMS
        • SELECT FOR UPDATE로 쓰기 잠금 걸고 조회하면 잠금 있는 읽기는 데이터 조회가 UNDO 로그가 아닌 테이블에서 수행되기 때문에 유령 읽기가 발생한다.
        • 잠금 있는 읽기는 테이블에 변경이 일어나지 않도록 테이블에 잠금을 걸고 테이블에서 데이터를 조회한다.
        • 잠금 없는 경우처럼 언두 로그를 바라보고 언두 로그를 잠그는 것은 불가능 - 언두 로그는 append only 형태로, 잠금 장치가 없기 때문이다.
        • 따라서 SELECT FOR SHARE/UPDATE로 레코드를 조회하는 경우에는 언두 영역의 데이터가 아니라 테이블의 레코드를 가져오게 되고, 이로 인해 유령 읽기가 발생한다.
      • MySQL
        • 갭 락이 존재하므로 유령 읽기가 발생하지 않는다.
        • SELECT FOR UPDATE로 조회 시, 해당 레코드에는 레코드 락, 범위에는 갭 락으로 넥스트 키 락을 건다. 다른 TX가 데이터 추가하려면 락 풀릴때까지 기다려야 함. => 유령 읽기 발생 불가능
        • 순수 SELECT -> 다른 TX가 데이터 추가 -> 잠금 SELECT인 경우에는 유령 읽기 발생 - 매우 드묾
        • SELECT FOR UPDATE 이후 SELECT: 갭락 때문에 팬텀리드 X
        • SELECT FOR UPDATE 이후 SELECT FOR UPDATE: 갭락 때문에 팬텀리드 X
        • SELECT 이후 SELECT: MVCC 때문에 팬텀리드 X
        • SELECT 이후 SELECT FOR UPDATE: 팬텀 리드 O
  • Serializable
    • 여러 트랜잭션이 동일한 레코드에 동시 접근할 수 없다.
    • 트랜잭션이 순차적으로 처리되어야 하므로 동시 처리 성능이 매우 떨어진다.
    • SELECT FOR SHARE/UPDATE는 대상 레코드에 각각 읽기/쓰기 잠금을 거는 것이다.
    • 하지만 순수한 SELECT 작업은 아무런 레코드 잠금 없이 실행되는데, SERIALIZABLE 격리 수준에서는 순수한 SELECT 작업에서도 대상 레코드에 넥스트 키 락을 읽기 잠금(공유락)으로 건다. 따라서 한 트랜잭션에서 넥스트 키 락이 걸린 레코드를 다른 트랜잭션에서느 절대 추가/수정/삭제할 수 없다. 가장 안전하지만 가장 성능이 떨어지므로 극단적으로 안전한 작업이 필요한 경우가 아니라면 사용해서는 안된다.

 

 

락(Lock)

  • 트랜잭션들이 동일한 데이터 항목에 대해 임의적인 병행 접근을 하지 못하도록 제어하는 것.
  • 락은 트랜잭션이 읽기를 할 때 사용하는 공유락과 읽고 쓰기를 할 때 사용하는 베타락으로 나뉜다.
  • 공유락(Shared-Lock)을 설정할 경우, 트랜잭션 T는 해당 데이터 항목에 대해 읽을 수 있지만 기록할 수 없다. 그리고 Read는 서로 영향을 주지 않으므로 다른 tx도 공유락이 설정된 X에 대해서 Shared-Lock을 동시에 설정할 수 있다.
  • 트랜잭션 T가 데이터 항목 X에 대해 베타락(Exclusive-Lock)을 설정할 경우 T는 읽을 수도, 기록할 수도 있다. Write는 영향을 주는 작업이므로 다른 트랜잭션은 베타락을 설정한 데이터 항목 X에 대해서 어떠한 Lock도 설정할 수 없다.

? 블로그 글 수정시 다른 사람이 읽기 가능한 건 베타락을 침범한거 아닌지?

 => 대부분의 DBMS는 배타적 락이 걸린 데이터도 공유 락을 통해 읽을 수 있게 설정되어 있다. 이러한 설정은 데이터의 일관성을 유지하면서도 동시성을 최대한 활용하기 위한 것. 그러나 DBMS의 설정과 사용하는 락의 종류에 따라 달라질 수 있다. 일부 시스템에서는 배타적 락이 걸린 데이터를 읽는 것 허용하지 않을 수 있다.

 

데드락 : 데드락 발생 시 일반적으로 DBMS가 하나를 강제로 중지시켜 한 트랜잭션은 정상적으로 실행되며 중지된 트랜잭션에서 변경한 데이터는 원래 상태로 되돌려 놓는다.

 

 

3. 트랜잭션 고립 수준

  • Dirty Read
    • 읽기 작업을 하는 tx1이 쓰기 작업을 하는 tx2가 작업한 중간 데이터를 읽기 때문에 발생하는 문제.
    • 작업중인 tx2가 작업을 롤백한 경우 tx1은 무효가 된 데이터를 읽게 되고 잘못된 결과를 도출한다.
    • = 무효가 된 데이터를 읽게 되어 발생하는 문제
  • Non-repeatable Read
    • tx1이 데이터를 읽고 tx2가 데이터를 쓰고(Update) tx1이 다시 한번 데이터를 읽을 때 생기는 문제
    • tx1이 읽기 작업을 다시 한 번 반복할 경우 이전의 결과와 다른 결과가 나오는 현상
  • Phantom Read
    • tx1이 데이터를 읽고 tx2가 데이터를 쓰고(Insert) tx1이 다시 한번 데이터를 읽을 때 생기는 문제
    • tx1이 읽기 작업을 다시 한 번 반복할 경우 이전에 없던 데이터(유령 데이터)가 나타나는 현상

 

트랜잭션 고립 수준 명령어(Transaction Isolation Level Instruction)

 - Lock보다는 완화된 방법으로 트랜잭션을 동시에 실행시키면서, 발생하는 문제를 해결하기 위해 DBMS가 제공하는 명령어.

 

  • READ UNCOMMITTED
    • 고립 수준 LEVEL 0, 자신의 데이터에 아무런 공유락을 걸지 않는다.
    • 배타락은 데이터의 갱신손실 문제 때문에 걸어주어야 한다.
    • 다른 tx에 공유락과 배타락이 걸린 데이터를 대기하지 않고 읽는다.
    • Select 문을 수행하는 경우 해당 데이터에 Shared-Lock이 걸리지 않는 LEVEL이다.
    • 어떤 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 B라는 아직 완료되지 않은 데이터(Dirty Read)를 읽을 수 있다.
  • READ COMMITTED
    • 고립 수준 LEVEL 1, Dirty Read의 참조를 피하기 위해 자신의 데이터를 읽는 동안 공유락을 걸지만 tx이 끝나기 전에라도 해지 가능하다.
    • 어떤 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안에 다른 사용자는 해당 데이터에 접근 불가.
  • REPEATABLE READ
    • 고립 수준 LEVEL 2, 자신의 데이터에 설정된 공유락과 배타락을 트랜잭션이 종료될 때까지 유지하여 다른 트랜잭션이 자신의 데이터를 갱신(UPDATE)할 수 없도록 한다.
    • 동시성이 낮아 특별하지 않은 상황이라면 사용하지 않는 것이 좋다.
    • TX이 완료될 때까지 SELECT 문이 사용하는 모든 데이터에 공유락이 걸리므로 다른 사용자는 그 영역의 데이터에 대한 수정이 불가능.
  • SERIALIZABLE
    • 고립 수준 LEVEL 3, 실행 중인 트랜잭션은 다른 트랜잭션으로부터 완벽하게 분리된다.
    • 데이터 집합에 범위를 지어 잠금 설정 가능 -> 다른 사용자가 데이터 변경하려고 할 때 tx를 완벽하게 분리 가능.
    • 가장 제한이 심하고 동시성이 낮다.
    • 즉 SELECT 문이 사용하는 모든 데이터에 공유락이 걸린다. SELECT의 대상이 되는 모든 테이블에 공유락을 설정하는 것과 같아서 나머지 범위에 대한 INSERT가 불가능하다.
    • 실행 중인 tx는 다른 tx로부터 완벽하게 분리된다.

 

 

4. 회복

 장애의 유형

  • 트랜잭션 장애 : tx 실행 시 논리적인 오류로 발생할 수 있는 에러 상황
  • 시스템 장애 : H/W 시스템 자체에서 발생할 수 있는 에러 상황
  • 미디어 장애 : 디스크 자체의 손상으로 발생할 수 있는 에러 상황

회복

  • 덤프 : 일정 주기로 원본의 데베의 모든 내용을 다른 저장장치에 복사
  • 로그 : 변경 이전의 데베를 기준으로 변경 연산이 발생할 때마다 로그 파일을 작성하여 기록하고, 회복할 때 로그에 적힌 내용을 사용하여 복원

로그 파일을 이용한 회복

- REDO : 장애가 발생한 후 시스템을 다시 가동했을 때, 로그 파일에 TX의 시작(START)이 있고, 종료(COMMIT)이 있는 경우 로그를 보면서 TX이 변경한 내용을 다시 기록하는 과정.

- UNDO : 장애가 발생한 후 시스템을 다시 가동했을 때, 로그 파일에 트랜잭션의 시작만 있고 종료가 없는 경우 완료하지 못했지만 버퍼의 변경 내용이 데베에 기록되어 있을 가능성이 있기 때문에 로그를 보면서 TX가 변경한 내용을 원상복구 시키는 과정이다.

 

로그 파일의 회복 방법

 - 즉시 갱신 : 갱신된 데이터를 로그에 기록하는 작업과 버퍼의 데이터를 데베에 옮기는 작업이 동시에 진행될 수 있으며 부분완료가 된 경우는 로그에 기록이 끝난 상태.

 - 지연 갱신 : 갱신된 데이터를 로그에 작성하는 작업이 끝난 후에야 버퍼의 데이터를 데베에 옮기는 작업이 진행될 수 있다.

 

체크포인트(CheckPoint, 검사점)

 - 로그는 그대로 기록을 유지하면서, 회복 관리자가 정하는 일정한 시간 간격으로 검사 시점을 생성하는 것

 - 회복 시 많은 양의 로그를 검색하고 갱신하는 시간을 줄이기 위함

 - 로그를 이용한 회복 기법이 좀 더 간단해짐.

 

주기억장치의 로그 레코드를 모두 하드디스크의 로그 파일에 저장한다.

그리고 tx 수행 중에 변경된 버퍼 내의 내용을 하드디스크의 데베에 저장한다.

 

 

핵심 : 체크포인트 이전까지는 체크를 했다. 이후는 아직 몰라.

1. 체크포인트 이전에 commit 기록이 있는 경우 : 아무 작업이 필요 없다.

2. 체크포인트 이후에 commit 기록이 있는 경우 : REDO를 사용한다. (T4, T5)

3. 체크포인트 이후에 commit 기록이 없는 경우 : 즉시 갱신 방법 사용 시 UNDO진행(T1, T6), 지연 갱신 방법 시 아무 작업도 해줄 필요 없음.(commit 이전에는 버퍼의 내용을 데베에 반영하지 않기 때문)

 

 

 

동시성 제어(Concurrency Control)

- DBMS가 다수의 사용자 사이에서 동시에 작용하는 다중 트랜잭션의 상호간섭 작용에서 데이터베이스를 보호하는 것.

 

낙관적 동시성 제어(Optimistic)

 - 사용자들이 같은 데이터를 동시에 수정하지 않을 것이라고 가정

 - 데이터를 읽는 시점에 Lock을 걸지 않는 대신 수정 시점에 값이 변경되었는지를 반드시 검사

 

비관적 동시성 제어(Pessimistic)

 - 사용자들이 같은 데이터를 동시에 수정할 것이라고 가정

 - 데이터를 읽는 시점에 Lock을 걸고, 트랜잭션이 완료될 때까지 이를 유지

 - 시스템의 동시성을 심각하게 떨어뜨릴 수 있어서 wait or nowaint 옵션과 함께 사용해야 함

 

비관적 동시성 제어를 위한 대표적인 방법 : Lock

공유락 - 읽기 잠금 = SELECT ... FOR SHARE

배타락 - 쓰기 잠금 = SELECT ... FOR UPDATE

 

Locking 메커니즘의 문제점

 - 읽기 작업과 쓰기 작업이 서로 방해를 일으키기 때문에 동시성 문제가 발생

 - 데이터 일관성에 문제가 생기는 경우도 있어서 Lock을 더 오래 유지하거나 테이블 레벨의 Lock을 사용해야 하고, 동시성 저하가 발생.

 

=> 이러한 문제점들 해결 위해 MVCC(Multi-Version Concurrency Control) 탄생.

 

 

MVCC(Multi-Version Concurrency Control) : 다중 버전 동시성 제어

- 동시 접근을 허용하는 데베에서 동시성을 제어하기 위해 사용하는 방법 중 하나.

- 데이터에 접근하는 사용자는 접근한 시점에 데베의 Snapshot을 읽는다. 이 Snapshot 데이터에 대한 변경이 완료(commit)될 때까지의 변경사항은 다른 데베 사용자가 볼 수 없다.

 - 이후에 사용자가 업데이터하면 이전의 데이터를 덮어 씌우는게 아니라 새로운 버전의 데이터를 UNDO 영역에 생성한다. 이렇게 해서 하나의 데이터에 대해 여러 버전의 데이터가 존재하게 되고, 사용자는 마지막 버전의 데이터를 읽게 된다.

 

- 일반적인 RDBMS보다 매우 빠르게 작동 - 잠금이 필요하지 않기 때문

- 사용하지 않는 데이터가 계속 쌓이게 되므로 데이터 정리하는 시스템이 필요

- 데이터 버전이 충돌하면 애플리케이션 영역에서 이러한 문제를 해결해야 함.

- 다른 사람이 그 데이터를 삭제하거나 수정하더라도 영향을 받지 않고 데이터를 사용할 수 있다.

- UNDO 블록 I/O, CR COPY 생성 등 부가적인 작업의 오버헤드 발생.

 

 

 

 

 

 

 

 

 

 

 

참조 : https://mangkyu.tistory.com/30

 

[Database] 8. 트랜잭션, 동시성 제어, 회복

[ 본 사진은 쉽게 배우는 오라클로 배우는 데이터베이스 개론과 실습 ppt에서 캡처했습니다. ]이번 장에서는 트랜잭션(Transaction), 동시성 제어(Locking or Currency Control), 회복(Recovery)에 대해 알아보겠

mangkyu.tistory.com

 

'Database' 카테고리의 다른 글

정규화  (0) 2024.05.15
인덱스(Index)  (0) 2024.02.21
NoSQL  (0) 2024.02.15

BELATED ARTICLES

more