[데이터베이스] 병행 제어

kkado·2022년 12월 1일
0

데이터베이스

목록 보기
4/4
post-thumbnail

데이터베이스를 공부하며 배운 내용들을 나름의 방식대로 정리합니다.

트랜잭션

일련의 연산들의 집합. 하나의 논리적 기능을 수행하기 위한 작업의 단위

  • 일관된 데이터베이스를 또 다른 일관된 상태로 변환시킴

트랜잭션의 모순

만약 A 계좌에서 B 계좌로 100원을 송금하는 트랜잭션이 있다고 했을 때, A 계좌의 잔고를 100만큼 감소시킨 후 B계좌의 잔고를 100만큼 증가시키기 전에 장애가 발생하면, A 계좌에는 잔고가 줄어들었지만 B 계좌에는 잔고가 늘어나지 않은 모순 상태 (inconsistent state가 발생함. 즉 둘 다 실행되지 않거나, 실행 될거면 둘 다 실행되어야 함. 이러한 모순 상태는 일관적이지 않다. 즉,

트랜잭션에 포함된 모든 연산은 완전 처리되거나 or 하나도 처리되지 않아야 하는 All or Nothing 방식으로 처리되어야 한다.

  • 트랜잭션은 Begin_trans ... End_trans로 묶어서 표시한다

트랜잭션의 특성 (ACID)

  • 원자성 (Atomicity) : 전부, 또는 전무 실행만 (all or nothing)
  • 일관성 (Consistency) : 트랜잭션 실행 후에도 일관성 유지
  • 격리성 (Isolation) : 트랜잭션 실행 중, 연산의 중간 결과에 다른 트랜잭션이 접근할 수 없음
  • 영속성 (Durability) : 트랜잭션이 일단 성공적으로 실행되면, 그 결과는 영구적임

즉 아까와 같은 송금 트랜잭션에서, A와 B의 잔고 합은 변하지 않아야 함

트랜잭션의 연산

Commit (완료)

트랜잭션이 성공적으로 실행되었음을 선언 -> 일관성 있는 데이터베이스 상태가 되었음
갱신된 데이터 값이 영속성을 갖도록 보장되어야 함

Rollback (복귀)

트랜잭션의 실행이 실패했음을 선언 -> 모순된 데이터베이스 상태
수행된 모든 연산 결과는 원상 복구 되어야 함

트랜잭션의 상태

  • 활동 (Active) : 트랜잭션이 실행을 시작함
  • 부분완료 (Partially Commited) : 마지막 명령문을 실행시킨 직후 상태
  • 완료 (Commited) : 실행을 성공적으로 완료. commit을 수행한 상태. DB에 저장됨
  • 실패 (Failed) : 실행 중에 장애나 오류 발생, 비정상적 상태
  • 철회 (Aborted) : 실행을 실패하여 rollback을 수행한 상태

병행 제어

복수 사용자를 지원하는 DBMS는 병행 수행(concurrency) 를 지원함
프로그램 P1, P2가 일정 시간을 간격으로 번갈아 가면서 실행되는 것을 인터리브(interleave) 형태 라고 한다.

병행제어의 문제점

갱신 분실 (lost update)

두 프로그램이 같은 변수에 접근할 때, 한 쪽의 갱신 결과가 덮어씌워져서 사라지는 것

모순성 (inconsistency)

연산 결과가 다른 프로그램의 실행에 의해서 사용자가 원하는 것이 아닌 값이 되는 것

연쇄 복귀 (cascading rollback)

트랜잭션 T1이 y를 읽으려고 할 때 문제가 발생했다면, 갱신을 취소하고 원상복귀 시켜야 한다. 이 중에서 T2도 원상복귀 되어야 하지만 이미 실행이 완료된 상태이다.

트랜잭션의 스케줄

직렬 스케줄(serial schedule)

  • 트랜잭션별로 순차적으로 실행
  • 이렇게 수행하면 어떤 경우에라도 데이터베이스에 대한 결과는 정확하다. 그러나 인터리빙을 허용하지 않는 직렬 스케줄의 가장 큰 문제점은 CPU의 낭비이다.

직렬 가능 스케줄(serializable schedule)

비직렬 스케줄 : 트랜잭션이 병행 수행함, CPU 활용도는 높일 수 있으나 그 결과값이 항상 정확하다고는 할 수 없다. 그러나 비직렬 스케줄로 실행되더라도 정확한 결과를 생성하는 경우가 있다.
그러나 비직렬 스케줄 중 어떤 것이 정확한 결과를 생성하고 어떤 것이 정확하지 않은 결과를 생성하는지 알아내는 것은 매우 어렵다.

병행 제어 기법

직렬 가능성 검사를 하지 않고도 직렬 가능성이 보장되는 방법. 타임스탬프 기법과 로킹 기법이 있다.

  • 로킹(locking) : 여러 트랜잭션들이 동일한 항목에 병행 접근하지 못하게 하는 것
  • 타임스탬프(time-stamp) : 시스템이 각 트랜잭션을 실행할 때 부여하는 고유 식별자

로킹(locking)

로킹은 병행 수행 시 공유 데이터를 제어하기 위해 lockunlock 연산을 사용한다.
이 기법은 다음과 같은 로킹 규약이 있으며 이 규약을 잘 지키면 두 개의 트랜잭션이 같은 데이터를 동시에 사용하는 경우는 없다.

  • 트랜잭션 T가 특정 데이터 항목 x에 대해 readwrite 연산을 수행하기 위해서는 먼저 lock 연산을 해야한다.
  • 트랜잭션 T가 실행한 lock에 대해 해당 트랜잭션이 모든 실행을 종료하기 전에 unlock 해야 한다.
  • 다른 트랜잭션에 의해 이미 lock이 걸려 있으면 lock을 할 수 없다.
  • 트랜잭션 T가 자신이 걸지 않은 lockunlock 할 수 없다.

이 때, 읽기(write)만을 수행한다면 데이터를 수정하지는 않기 때문에 굳이 접근을 통제할 필요가 없다. 그렇게 되면 lock 연산은 다음과 같이 공용 로크와 전용 로크로 나눌 수 있다.

  • lock-S : 공용 로크(Shared lock) : 트랜잭션이 데이터 x에 대해 lock-S를 설정할 경우, 이 트랜잭션은 x에 대해 read는 가능하나 write할 수 없다. 이 때 다른 트랜잭션 또한 데이터 x에 대해 lock-S를 걸 수 있다.
  • lock-X : 전용 로크(Exclusive lock) : 트랜잭션이 데이터 x에 대해 lock-S를 설정하면, 트랜잭션은 이 항목에 대해 readwrite 모두 할 수 있고, 다른 트랜잭션은 x에 대해 어떠한 lock도 할 수 없다.

lock 연산의 양립성

두 트랜잭션 모두 공용 로크일 때만 양립이 가능하다. 즉 두 트랜잭션 모두 write는 할 수 없고 read만 가능한 상황에서만 양립 가능하다.

조금 생각해보면 당연한 말인데 한 쪽에서 lock-X를 할 경우 write할 수 있기 때문에 반대편에서는 read조차 하면 안된다.


2단계 로킹 규약

그러나 로킹 규약을 따랐음에도 모순성이 발생할 수 있다. 직렬 가능성을 보장하기 위해서는 추가적인 약속이 필요한데 이러한 문제점을 해결하기 위해 2단계 로킹 규약(2PLP : Two Phase Locking Protocol)을 적용한다.

이 규약에서 모든 트랜잭션은 다음과 같이 2단계로 수행할 것을 요구한다.

  • 확장 단계 : 트랜잭션이 lock 연산만 수행할 수 있음
  • 축소 단계 : 트랜잭션이 unlock 연산만 수행할 수 있음

스케줄 내의 모든 트랜잭션들이 2단계 로킹 규약을 준수하면 그 스케줄은 직렬 가능하며, 따라서 정확한 결과를 생성한다.

교착 상태(deadlock)

그러나 2PLP는 교착 상태(deadlock)이 발생할 수 있는 가능성을 내포한다.

교착 상태란 둘 이상의 트랜잭션이 서로 상대가 갖고 있는 데이터 항목의 로크가 해제되기를 기다리기 때문에 트랜잭션의 실행이 중단되고 무한정 기다리는 상태를 의미한다.

교착 상태가 일어나는 조건은 다음과 같다.

  • 상호 배제 (Mutual Exclusion)
  • 대기 (Wait for)
  • 비선점 (No Preemption)
  • 순환 대기 (Circle wait)

이 4가지 조건 중 하나라도 만족하지 않으면 발생하지 않는다.

교착 상태 해결 방법

  • 회피 : 자원을 할당할 때마다 deadlock이 일어나지 않게 감시하는 방법. 각 트랜잭션의 타임스탬프를 이용하는 방법이 있다.

    • wait-die 기법 : 트랜잭션 T1가 이미 트랜잭션 T2에 의해 lock한 데이터 항목을 요구할 때 만약 T1의 타임스탬프가 더 작을 경우, T1을 기다리게 하고, T1의 타임스탬프가 더 클 경우 T1을 포기하게 한다.
    • wound-wait 기법 : 트랜잭션 T1가 이미 트랜잭션 T2에 의해 lock한 데이터 항목을 요구할 때 만약 T1의 타임스탬프가 더 작을 경우, T1은 데이터를 선점 하고, T1의 타임스탬프가 더 클 경우 T1을 기다리게 한다.
  • 예방 : 트랜잭션을 실행하기 전에 필요한 모든 lock을 한번에 할당받고 전부 부여받지 못한다면 실행하지 않는 방법. 실현 가능성은 매우 낮다.

  • 탐지 : lock 상태를 조사하여 일단 교착 상태가 발생하면 트랜잭션 중 하나를 취소시키는 것. 교착 상태를 탐지하는 방법은 대기 그래프를 그려보는 것이 효과적이며, 이 때 취소시킬 트랜잭션은 작업이 가장 적게 수행된 트랜잭션이 선정되는 것이 효율적이다.

데드락에 대한 보다 자세한 설명은 여기 를 참조 ...

로킹 단위

로킹 단위(locking granularity)란 로킹의 대상이 되는 데이터 객체의 크기를 말하며, 이는 곧 병행 제어의 데이터 단위가 된다.

일반적으로 로킹 단위가 클수록 병행성 수준이 낮아지지만 병행 제어 기법은 간단해진다.
로킹 단위가 작을수록 병행성 수준은 높아지지만 lock의 수가 많아져 제어가 복잡해진다.

극단적으로, 로킹 단위가 데이터베이스 전체라면 병행성은 없다.
시스템의 성능을 향상시키기 위하여 적절한 로킹 단위의 선정이 중요하다.

지금까지 알아본 로킹 단위는 데이터 항목만을 고려하였으나 레코드 단위, 파일 단위 등의 로킹 단위를 지원할 수 있는 다중 단위 로킹(multiple granularity locking) 기법을 사용한다면 이러한 비효율성을 개선할 수 있다.

profile
울면안돼 쫄면안돼 냉면됩니다

0개의 댓글