Computer Science/Database

[Database] 트랜잭션

aajin126 2023. 5. 31. 19:08

DBMS는 디스크에 갈 일이 많다. (사용자들이 요청하는 데이터를 가져와야 하고, 큰 데이터들이 많이 저장되어 있으므로)

→ I/O 데이터를 가져오는 일은 오래 걸리므로 한 사용자가 요청한 I/O task를 끝내기를 기다리고만 있을 수 없기 때문에 동시성을 관리한다.

 

트랜잭션 개요

  • 트랜잭션(transaction)
    • 프로그램에서 논리적인 단위의 갱신 명령의 모음이다.
    • 많은 사용자들이 동시에 데이터베이스의 서로 다른 부분 또는 동일한 부분을 접근하면서 데이터베이스를 하용한다.
  • 동시성 제어(concurrency control)
    • 동시성 제어의 목표 : 실제 처리된 일이 마치 한 명 한 명을 순차적으로 처리한 것과 동일하게 처리해주는 것이 동시성 제어의 목적이다.
    • 즉, 동시성 제어를 통하여 다수의 사용자가 데이터베이스를 동시에 접근하도록 허용하면서 데이터베이스의 일관성을 유지해준다.
  • 회복(recovery)
    • 데이터베이스를 갱신하는 도중에 시스템이 고장나도 데이터베이스의 일관성을 유지한다.
  • 예시1
    • 사원이 500명 재직하고 있는 회사에서 모든 사원의 급여를 6% 인상하는 연산을 수행한다.
      • 500명 전원의 급여가 수정되거나 한 명도 수정되지 않도록 DBMS가 보장해야 한다.
      • 수정 중간에 컴퓨터 시스템이 다운된 후에 재기 되었을 때에 DBMS는 어떻게 대응해야 하는가?
        • DBMS가 추가로 정보를 유지하지 않는다면 DBMS가 재기동된 후에 어느 직원까지 수정되었는가를 알 수 없다. → 로그(log) 유지로 해결할 수 있음.
  • 예시2
    • 은행 고객 정미림은 자신의 계좌에서 10만원을 인출하여 안명석의 계좌로 이체하려고 한다.
      • 첫 번째 UPDATE(정미림 -10만)가 실행된 후에 두 번째 UPDATE(안명석 +10만)가 실행되기 전 컴퓨터 시스템이 다운되면 재기동한 후에 DBMS가 어떻게 대응해야 하는가?
        • 위 두개의 UPDATE문은 둘 다 완전하게 수행되거나 한 UPDATE 문도 수행되어서는 안되도록, 하나의 트랜잭션(단위)처럼 DBMS가 보장해야 한다.
        • 기본적으로는 각각의 SQL 문이 하나의 트랜잭션으로 취급된다.
        • 두 개 이상의 SQL문들을 하나의 트랜잭션으로 취급하려면 사용자가 이를 명시적으로 표시해야 한다.

트랜잭션의 특성(ACID 특성)

  • 원자성(Atomicity)
    • 한 트랜잭션 내의 모든 연산들이 완전히 수행되거나 전혀 수행되지 않음(all or nothing)
    • DBMS의 회복 모듈은 시스템이 다운되는 경우, 부분적으로 데이터베이스를 갱신한 트랜잭션의 영향을 취소하여 트랜잭션의 원자성을 보장한다.
    • 완료된 트랜잭션이 갱신한 사항은 트랜잭션의 영향을 재수행하여 트랜잭션의 원자성을 보장한다.
  • 일관성(Consistency)
    • 어떤 트랜잭션이 수행되기 전 데베가 일관된 상태를 가졌다면 트랜잭션이 수행된 후에는 또 다른 일관된 상태를 가져야한다. (트랜잭션 수행 중에는 일시적으로 일관된 상태가 아닐 수 있다.)
  • 고립성(Isolation)
    • 한 트랜잭션이 데이터를 갱신하는 동안 이 트랜잭션이 완료되기 전에는 갱신 중인 데이터를 다른 트랜잭션들이 접근하지 못하도록 해야 한다.
    • 다수의 트랜잭션들이 동시에 수행되더라도 그 결과는 어떤 순서에 따라 트랜잭션들을 하나씩 차례대로 수행한 결과와 같아야 한다. 다른 트랜잭션들이 어떤 트랜잭션과 같이 수행되고 있음을 서로서로 몰라야 한다.
    • DBMS의 동시성 제어 모듈이 트랜잭션의 고립성을 보장하고 DBMS는 응용들의 요구사항에 따라 다양한 고립 수준(isolation level)을 제공한다.
  • 지속성(Durability)
    • 일단 한 트랜잭션이 완료되면 이 트랜잭션이 갱신한 것은 그 후에 시스템에 고장이 발생하더라도 손실되지 않는다. (디스크에까지 write되지 않는 경우 방지)
    • 완료된 트랜잭션의 효과는 시스템이 고장난 경우에도 데이터베이스에 반영된다.
    • DBMS의 회복 모듈은 시스템이 다운되는 경우에도 트랜잭션의 지속성을 보장한다.
  • COMMIT : 트랜잭션의 완료
    • 트랜잭션에서 변경하려는 내용이 DB에 완전하게 반영된 상태
    • DB가 새로운 일관된 상태를 가진다.
  • ROLLBACK : 트랜잭션의 철회(abort)
    • 트랜잭션에서 변경하려는 내용이 DB에 일부만 반영된 경우 원자성을 보장하기 위해 트랜잭션이 수행되기 전 상태로 되돌린다.
    • DB가 불일치 상태를 가진다.
  • 트랜잭션이 성공하지 못하는 원인
    • 시스템 고장 (중앙 처리 장치, 주기억 장치, 전원 공급 장치 고장)
    • 트랜잭션 고장 (트랜잭션이 수행되는 도중에 ROLLBACK이 일어난다.)
    • 매체 고장(디스크 고장 → 주기적으로 DB는 백업을 하기 때문에 백업 내용을 가지고 recovery 해야함, DB의 recovery로는 회복이 안됨)
    • 통신 고장, 자연적 재해, 부주의 등등

동시성 제어

여러 사용자들이 다수의 트랜잭션들을 동시에 수행하는 환경에서 트랜잭션 간의 간섭이 생기지 않도록 한다. (여러 가지의 트랜잭션이 같은 테이블을 접근할 때 나 혼자 쓰는 것처럼, 순서대로 수행하는 것처럼 결과를 생성해주는 것)

<데이터베이스 연>

Output (주기억장치 → DB) : 주기억 장치의 블록을 디스크에 기록한다.

Input (DB → 주기억장치) : 디스크의 블록을 주기억 장치의 버퍼로 읽어들인다.

**read_item(주기억 장치 → 프로그램) :**주기억 장치의 데이터를 프로그램 변수로 복사

write_item(프로그램 → 주기억 장치) : 변수 값이 프로그램에서 정해지면 주기억 장치에 저장

동시성 제어를 하지 않고 트랜잭션을 수행할 때 생길 수 있는 문제

  • 오손 데이터 읽기(dirty read) : 완료되지 않은 트랜잭션이 갱신한 데이터를 읽는 것
  • 갱신 손실(lost update) : 수행 중인 트랜잭션이 갱신한 내용을 다른 트랜잭션이 덮어 씀으로 갱신이 무효가 되는 것
  • 반복할 수 없는 읽기(unrepeatable read) : 한 트랜잭션이 동일한 데이터를 읽을 때 서로 다른 값을 읽는 것

로킹(locking)

  • 동시성 제어를 위해 가장 널리 사용되는 기법
  • lock(로크)은 데이터베이스 내의 각 데이터 항목과 연관된 하나의 변수처럼 생각된다.
  • 각 트랜잭션이 수행을 시작하여 데이터 항목을 접근할 때마다 요청한 로크에 관한 정보는 로크 테이블 등에 유지된다.
    • 갱신 목적 : 독점 로크(X-lock, exclusive lock)
    • 읽을 목적 : 공유 로크(S-lock, shared lock)
    • 데이터에 대한 접근을 끝낸 후에는 로크를 해제(unlock)

2단계 로킹 프로토콜(2-phase locking protocol)

  • 로크(lock)를 요청하는 것과 로크를 해제하는 것이 2단계로 이루어진다.
  • 로크(lock)를 거는 확장 단계가 지난 후에 로크(lock)을 해제하는 수축 단계에 들어가야 한다.
  • 일단 로크를 한 개라도 해제하면 lock 수축 단계에 들어간다.

로크 확장 단계(1단계)

  • 새로운 로크를 요청할 수는 있지만 보유하고 있던 로크를 하나라도 해제할 수 없음

로크 수축 단계(2단계)

  • 보유하고 있던 로크를 해제할 수 있지만 새로운 로크를 요청할 수 없음
  • 로크를 조금씩 해제할 수도 있고, 트랜잭션이 완료된 시점에 한꺼번에 모든 로크를 해제할 수도 있음.

lock point(로크 포인트) : 한 트랜잭션에서 필요로하는 모든 로크를 걸어 놓은 시점을 말한다.

데드록 (deadlock)

2단계 로킹 프로토콜에서는 데드록이 발생할 수 있다.

  • T1과 T2는 모두 X,Y를 갱신할 수 있다고 할 때, T1이 먼저 X 로크 허가를 받아 갱신 작업을 하고 있고 Y는 Y 로크 허가를 받아 갱신 작업을 하고 있다.
  • 이 때, T1이 T2가 작업 중인 Y에 로크 요청을 하면 Y를 갱신하고 있는 T2의 작업이 끝날 때까지 기다려야하고 동시에 T2도 T1이 작업 중인 X에 대해 로크 요청을 했다면 X와 Y는 서로 갱신 작업 중인 T1과 T2의 로크가 끝나기를 영원히 기다려야 하는 것이다.
  • 이처럼, 두 개 이상의 트랜잭션들이 서로 상대방이 보유하고 있는 로크를 요청하면서 기다리고 있는 상태데드록이라고 한다.데드록을 해결하기 위해서는 데드록을 방지하는 기법이나, 데드록을 탐지하고 어떤 트랜잭션이든 희생자를 선정하여 데드록을 푸는 기법 등을 사용한다.

다중 로크 단위 (multiple granularity)

  • 대부분의 트랜잭션이 소수의 투플들을 접근하는 데이터베이스 응용에서는 투플 단위로 로크를 해도 로크 테이블을 다루는 시간이 오래 걸리지 않는다.
  • 트랜잭션이 많은 투플을 접근하는 데이터베이스 응용에서 투플 단위로만 로크를 한다면 로크 테이블에서 로크 충돌을 검사하고, 로크 정보를 기록하는 시간이 오래 걸린다.
  • 트랜잭션이 접근하는 투플의 수에 따라 로크를 하는 데이터 항목의 단위를 구분하는 것이 필요하다.
  • 한 트랜잭션에서 로크할 수 있는 데이터 항목이 두 가지 이상 있으면 다중 로크 단위라고 말한다.
  • 데이터베이스에서 로크할 수 있는 단위로는 데이터베이스, 릴레이션, 디스크 블록, 투플 등이 있다.
  • 일반적으로 DBMS는 각 트랜잭션에서 접근하는 투플 수에 따라 자동적으로 로크 단위를 조정한다.
  • 로크 단위가 작을수록 로킹에 따른 오버헤드가 증가하고 동시성의 정도가 증가한다.

팬텀 문제(phantom problem)

  • 시간 1에 트랜잭션 T1의 SELECT문이 수행되면 1번 부서에 근무하는 사원들의 이름인 박영권, 김상원이 검색된다.
  • 시간 2에 트랜잭션 T2의 INSERT문이 수행되면 EMPLOYEE 릴레이션에 1번 부서에 근무하는 정희연 투플이 삽입된다.
  • 시간 3에 트랜잭션 T1의 두 번째 SELECT문이 수행되면 박영권, 김상원, 정희연이 검색된다.
  • 한 트랜잭션 T1에 속한 첫 번째 SELECT문과 두 번째 SELECT문의 수행 결과가 다르게 나타난다. (정희연의 data가 유령 data가 된 것)

공유 lockINSERT가 아무 상관이 없기에 발생하는 문제이다.

해결 방법 : 공유 lock을 걸 때 릴레이션의 INSERT를 막는다. (동시성을 감소시킨다.)

회복

성능 향상을 위해서는 버퍼의 내용을 디스크에 기록하는 것을 가능하면 최대한 줄이는 것이 일반적이다. (버퍼가 꽉 차거나 트랜잭션이 완료될 때 버퍼의 내용이 디스크에 기록될 수 있다.)

  • 트랜잭션이 버퍼에는 갱신 사항을 반영했지만 버퍼의 내용이 디스크에 기록되기 전에 고장이 발생할 수 있다는 문제점이 있다.
  • 고장이 발생하기 전에 트랜잭션이 완료 명령(COMMIT)을 수행했다면 회복 모듈은 이 트랜잭션의 갱신 사항을 **재수행(REDO)**하여 트랜잭션의 갱신이 지속성을 갖도록 해야 한다.
  • 고장이 발생하기 전에 트랜잭션이 완료 명령을 수행하지 못했다면 원자성을 보장하기 위해서 이 트랜잭션이 데이터베이스에 반영했을 가능성이 있는 갱신 사항을 취소(UNDO) 해야 한다. → 일부 수행된 갱신 사항을 취소한다.

<저장 장치 유형>

  • 주기억 장치와 같은 휘발성 저장 장치에 들어 있는 내용은 시스템이 다운된 후에 모두 사라진다.
  • 디스크와 같은 비휘발성 저장 장치에 들어 있는 내용은 디스크 헤드 등이 손상을 입지 않는 한 시스템이 다운된 후에도 유지된다.
  • 안전 저장 장치(stable storage)는 모든 유형의 고장을 견딜 수 있는 저장 장치를 의미한다.
  • 두 개 이상의 비휘발성 저장 장치가 동시에 고장날 가능성이 매우 낮으므로 비휘발성 저장 장치 두 개 이상의 사본을 중복해서 저장함으로써 안전 저장 장치를 구현한다.

<재해적 고장과 비재해적 고장>

  • 재해적 고장
    • 디스크가 손상을 입어서 데이터베이스를 읽을 수 없는 고장
    • DB 백업 자기 테이프를 기반으로 회복해야 한다.
  • 비재해적 고장
    • 재해적 고장 이외의 고장
    • 대부분의 회복 알고리즘들은 비재해적 고장에 적용된다.
    • 로그를 기반으로 한 즉시 갱신, 로그를 기반으로 한 지연 갱신, 그림자 페이징(shadow paging) 등 여러 알고리즘
    • 대부분의 상용 DBMS에서 로그를 기반으로 한 즉시 갱신 방식을 사용한다.

<로그를 사용한 즉시 갱신>

즉시 갱신에서는 트랜잭션이 데이터베이스를 갱신한 사항이 주기억 장치의 버퍼에 유지되다가 트랜잭션이 완료되기 전이라도 디스크의 데이터베이스에 기록될 수 있다. (COMMIT 하기 전이라도 버퍼가 가득 차면 디스크에 반영한다.)

데이터베이스에는 완료된 트랜잭션의 수행 결과뿐만 아니라 철회된 트랜잭션의 수행 결과도 반영될 수 있다.

트랜잭션의 원자성과 지속성을 보장하기 위해 DBMS는 로그라고 부르는 특별한 화일을 유지한다.

  • DB의 항목에 영향을 미치는 모든 트랜잭션의 연산에 대해 로그 레코드를 기록한다.
  • 각 로그 레코드는 로그 순서 번호(LSN: Log Sequence Number)로 식별된다.
  • 주기억 장치 내의 로그 버퍼에 로그 레코드들을 기록하고 로그 버퍼가 꽉 찰 때 디스크에 기록한다.
  • 로그는 데이터베이스 회복에 필수적이므로 일반적으로 안전 저장 장치에 저장된다.
    • 이중 로그(dual logging) : 로그를 두 개의 디스크에 중복해서 저장하는 것
  • 각 로그 레코드가 어떤 트랜잭션에 속한 것인가를 식별하기 위해 각 로그 레코드마다 트랜잭션 ID를 포함시킨다.
  • 동일한 트랜잭션에 속하는 로그 레코드들은 연결 리스트로 유지된다.
    <로그 레코드 유형>
    • [Trans-ID, start]
      • 한 트랜잭션이 생성될 때 기록되는 로그 레코드
    • [Trans-ID, old-value, new-value]
      • 주어진 Trans_ID를 갖는 트랜잭션이 데이터 항목 X를 이전 값에서 새 값으로 수정했음을 나타내는 로그 레코드
    • [Trans-ID, commit]
      • 주어진 Trans_ID를 갖는 트랜잭션이 데이터베이스에 대한 갱신을 모두 성공적으로 완료하였음을 나타내는 로그 레코드
    • [Trans-ID, abort]
      • 주어진 Trans_ID를 갖는 트랜잭션이 철회되었음을 나타내는 로그 레코드

<트랜잭션의 완료점(commit point)>

  • 한 트랜잭션의 데이터베이스 갱신 연산이 모두 끝나고 데이터베이스 갱신 사항이 로그에 기록되었을 때 그 트랜잭션이 완료점에 도달한다.
  • 시스템이 다운된 후에 재기동되면, 회복과정을 거쳐야 한다.
    • DBMS의 회복 모듈은 로그를 검사하여 로그에 [Trans-ID, start] 로그 레코드와 [Trans-ID, commit] 로그 레코드가 모두 존재하는 트랜잭션들은 재수행된다.
    • [Trans-ID, start] 로그 레코드는 로그에 존재하지만 [Trans-ID, commit] 로그 레코드가 존재하지 않는 트랜잭션들은 취소한다.

로그 먼저 쓰기(WAL: Write-Ahead Logging)

  • 트랜잭션이 데이터베이스를 갱신하면 주기억 장치의 데이터베이스 버퍼에 갱신 사항을 기록하고, 로그 버퍼에는 이에 대응되는 로그 레코드를 기록한다.
  • 만일 데이터베이스 버퍼가 로그 버퍼보다 먼저 디스크에 기록되는 경우에는 로그 버퍼가 디스크에 기록되기 전에 시스템이 다운될 수 있다. 로크 레코드가 없어서 이전값을 알 수 없으므로 트랜잭션의 취소가 불가능하다.
  • 따라서 데이터베이스 버퍼보다 로그 버퍼를 먼저 디스크에 기록해야 한다.
  • 데이터 베이스 버퍼 + 로그 버퍼 = 주기억장치 버퍼

체크포인트(checkpoint)

체크포인트를 할 때 수행되는 작업

  • 수행 중인 트랜잭션들을 일시적으로 중지시킨다. 회복 알고리즘에 따라서는 이 작업이 필요하지 않을 수 있다.
  • 주기억 장치의 로그 버퍼를 디스크에 강제로 출력한다.
  • 주기억 장치의 데이터베이스 버퍼를 디스크에 강제로 출력한다.
  • [checkpoint] 로그 레코드를 로그 버퍼에 기록한 후 디스크에 강제로 출력한다.
  • 체크포인트 시점에 수행 중이던 트랜잭션들의 ID도 [checkpoint] 로그 레코드에 함께 기록한다.
  • 일시적으로 중지된 트랜잭션의 수행을 재개한다.