본문 바로가기
데이터베이스

[관계형 데이터베이스] - 동시성 (Concurrency)

by CHML 2016. 12. 17.
1. 데이터베이스에서의 동시성

데이터베이스는 다수의 사용자들이 동시에 접근하는 경우가 빈번하게 발생한다. 그러나 여러 사용자가 동시에 데이터베이스에 접근하는 상황에서 사용자들에 대한 적절한 통제가 이루어지지 않는다면, 데이터베이스의 무결성이 깨지고, 어떠한 transaction의 수행에 대해 의도하지 않은 결과가 반환될 수도 있다.

DBMS (Database Management System)는 동시성 제어 (concurrency control)라는 기능을 제공하여 데이터베이스의 무결성을 보호하고, transaction이 항상 정확하고 일관된 데이터를 참조할 수 있도록 해야 한다.


2. 직렬성 (Serializability)과 동시성 (Concurrency)

데이터베이스에서 직렬성 (serializability)은 각각의 transaction이 일정한 순서를 가지고 순차적으로 실행되는 것을 의미한다. 이와 반대로 동시성 (concurrency)은 transaction들이 순차적으로 실행되는 것이 아니라, transaction을 구성하는 각각의 쿼리문들이 transaction의 순서에 상관없이 동시에 실행되는 것을 의미한다.


3. 동시성 문제

Transaction이 동시에 실행되는 환경에서는 다음과 같은 세 가지 문제가 발생할 수 있다.


1) The Lost Update Problem

예를 들어, 두 개의 transaction이 동시에 실행되고 있는 [그림 1]과 같은 상황이 있다. 이 예제에서는 ${t}_{3}$ 시점에 Transaction A가 $x$라는 레코드의 값을 변경하였고, 그 다음에 Transaction B가 $x$의 값을 변경하였다.


[그림 1] UPDATE 연산이 중첩되는 상황


다수의 transaction이 동시에 실행되는 환경에서 위와 같이 서로 다른 transaction이 UPDATE 연산을 연속으로 수행하면, 먼저 실행된 UPDATE 연산이 overwriting 된다. 즉, 먼저 실행된 transaction A는 정상적으로 실행되었으나, $x$의 값은 변경되지 않는 문제가 발생한다.


2) The Uncommitted Dependency Problem

이러한 문제는 transaction의 실행 중에 오류가 발생하여 ROLLBACK이 실행되면 발생한다. 아래의 [그림 2]는 uncommitted dependency problem의 한 예시를 보여준다.


[그림 2] Transaction의 실행 취소로 인해 문제가 발생하는 상황


위의 [그림 2]에서는 Transaction B가 $x$의 값을 변경한 다음, 변경된 $x$의 값을 Transaction A가 읽은 상황에서 Transaction B의 실행이 취소되었다. 만약, [그림 2]와 같은 상황이 발생하면, 실제로 데이터베이스에 저장된 $x$의 값과 Transaction A가 읽은 $x$의 값이 일치하지 않는 문제가 발생한다.


3) The Inconsistent Analysis Problem

이러한 문제는 한 transaction의 실행 중에 다른 transaction의 내용이 실행되어 데이터베이스의 일관성이 깨지는 현상을 말한다. 예를 들어, 다음의 [그림 3]과 같은 상황에서는 Transaction A의 실행에 의해 $sum$의 값이 120이 아니라, 110이 되는 문제가 발생한다.


[그림 3] 의도되지 않은 결과가 도출되는 상황


위의 [그림 3]과 같은 상황에서는 Transaction A가 $var3$를 읽기 전에 Transaction B가 UPDATE 연산을 실행하여 $sum$이 110이 되는 잘못된 결과가 발생한다.


4. 교착 상태 (Deadlock)

데이터베이스에서 교착 상태 (deadlock)는 두 개 이상의 transaction이 서로 lock이 해제되기를 기다리면서 무한정 기다리는 상황을 말한다. 교착 상태로 인한 문제를 해결하기 위한 방법으로는 크게 다음과 같은 세 가지가 있다.


  • 교착 상태 예방 (prevention): 교착 상태가 발생하는지를 미리 검사하여 교착 상태가 발생하지 않는 요청만 수용하는 방법

  • 교착 상태 회피 (avoidance): 요청을 수행하면서 교착 상태가 발생하는 상황이 오면 특정 방법을 통해 교착 상태를 회피하는 방법

  • 교착 상태 탐지 (detection) 및 회복: 교착 상태가 발생하는 것을 허용하며, 교착 상태를 탐지하는 알고리즘을 통해 교착 상태가 발생한 것을 탐지하면, 교착 상태 회복 알고리즘을 통해 교착 상태를 제거하는 방법


교착 상태 회피를 이용하는 방법에서는 큐와 힙 등을 이용한 우선순위의 개념을 통해 교착 상태를 회피하기도 한다.


6. Serializable

어떠한 transaction들이 중첩되어 실행되는 것과 순차적으로 실행되는 것의 결과가 같을 때 serializable 이라고 한다. Serializable은 two-phase locking protocol에 의해 보장될 수 있다.

Two-phase locking protocol은 동시성 제어를 위해 상호 배제 기능을 제공하는 기법이다. Two-phase locking protocol은 아래와 같은 두 단계로 구성되어 있다.


  • 확장 단계 (growing phase): 이 단계에서는 transaction이 lock을 설정하는 잠금 연산만 수행할 수 있으며, lock을 제거하는 해제 연산은 수행할 수 없다.

  • 축소 단계 (shrinking phase): 이 단계에서는 transaction이 해제 연산만 수행할 수 있으며, 잠금 연산은 수행할 수 없다.


Two-phase locking protocol을 따르는 transaction은 READ, UPDATE 연산을 수행하기 전에 반드시 lock을 설정해야 하며, transaction이 종료되기 전에는 반드시 lock을 제거해야 한다. Transaction은 READ 연산 시에 s-lock을 설정하고, UPDATE 연산 시에는 x-lock을 설정한다. 여기에서 말하는 s-lock과 x-lock은 시스템소프트웨어 및 운영체제에서 말하는 s-lock 및 x-lock과 동일한 성질을 갖는다.

7. 동시성 제한 수준

위에서 언급한 바와 같이 데이터베이스에서는 동시성을 제한하여 올바르게 데이터베이스에 대한 연산이 수행되도록 한다. 동시성을 제한하는 방법에도 제한 수준이 있으며, 그 내용은 다음과 같다.


  • repeatable read (RR): Transaction들의 실행 순서가 모두 serializable한 것을 말한다. 이러한 제한 수준에서는 하나의 레코드에 대한 lock이 transaction이 끝날 때까지 유지된다. 위에서 언급한 two-phase locking protocol과 매우 유사하다.
  • cursor stability (CS): 위에서 말한 repeatable read처럼 transaction이 끝날 때까지 lock이 유지되는 것이 아니라, 하나의 레코드에 대한 연산이 수행될 때 lock이 설정되고, 해당 레코드에 대한 연산이 끝나면 설정된 lock이 제거되는 제한 수준이다.


위에서 설명한 cursor stability 수준의 동시성 제한은 two-phase locking protocol을 따르지 않기 때문에 더 이상 serializability를 보장하지 않는다.


8. SQL에서의 transaction 격리 수준

동시성을 지원하는 데이터베이스에서는 위에서 말한 repeatable read 및 cursor stability와 같이 transaction을 일정 규칙에 따라 격리한다. 이러한 transaction의 격리 수준에 따라 바람직하지 않은 세 가지 읽기 연산이 발생할 수 있는데, 그 내용은 다음과 같다.


  • dirty read: transaction A가 $x$를 ${x}^{'}$으로 변경한 다음, transaction B가 ${x}^{'}$을 읽은 상황에서 transaction A가 ROLLBACK을 하는 경우를 말한다.

  • non-repeatable read: transaction A가 $x$를 여러 번 조회하는 중에 transaction B가 $x$의 값을 변경하여 $x$의 값이 서로 다르게 나오는 경우를 말한다.

  • phantom read: transaction A가 레코드를 여러 번 조회하는 중에 transaction B가 새로운 레코드를 추가하여, 이전에는 존재하지 않았던 레코드가 갑자기 나타나는 경우를 말한다. Phantom read를 해결하기 위한 방법으로는 테이블 단위의 lock 설정, index에 해당하는 필드에 lock을 설정하는 방법 등이 있다.


위에서 말한 세 가지 바람직하지 않은 읽기 연산은 아래의 [표 1]과 같은 transaction의 격리 수준에 따라 나타날 수 있다.


[표 1] SQL의 격리 수준


위의 [표 1]에 있는 격리 수준에 따라 바람직하지 않은 읽기 현상이 일어나는 이유는 다음과 같다.


  • SERIALIZABLE: 데이터베이스에서 serializable 하다는 것은 transaction들이 동시적으로 실행되는 것과 순차적으로 실행되는 것이 같은 경우에 해당하기 때문에 동시성으로 인한 바람직하지 않은 읽기가 발생하지 않는다.

  • REPEATABLE READ: 이 격리 수준에서는 한 번 lock이 설정된 레코드는 해당 transaction이 종료될 때까지 lock이 유지되기 때문에 dirty read와 non-repeatable read가 발생하지 않는다.

  • READ COMMITTED: 많은 SQL 시스템에서 기본값으로 사용하는 격리 수준으로, transaction이 어떤 데이터에 대해 UPDATE 연산을 실행하는 동안, 다른 transaction은 해당 데이터에 접근할 수 없다. 즉, 해당 격리 수준에서는 transaction A가 $x$를 읽고, transaction B가 $x$를 수정한 다음에 다시 transaction A가 $x$를 읽는 것이 가능하며, 이는 non-repeatable read가 발생하는 상황에 해당한다.

  • READ UNCOMMITTED: 가장 낮은 격리 수준을 갖는 단계로써, 어떠한 transaction이 데이터를 수정하는 동안 다른 transaction이 수정되고 있는 데이터를 읽을 수 있다.