직렬 가능 스케줄

서버란·2024년 10월 14일

CS 지식

목록 보기
24/25

직렬 가능 스케줄(Serializable Schedule)은 데이터베이스 트랜잭션의 동시성 제어에서 매우 중요한 개념입니다. 여러 트랜잭션이 동시에 실행될 때, 트랜잭션들의 실행 순서를 제어하여 데이터의 일관성을 보장하는 방법 중 하나입니다. 직렬 가능 스케줄은 직렬 실행(Serial Execution)과 같은 결과를 내는 동시 실행 스케줄을 의미합니다.

1. 직렬 스케줄과 직렬 가능 스케줄

  • 직렬 스케줄(Serial Schedule):

    트랜잭션들이 순차적으로 하나씩 실행되는 스케줄을 의미합니다. 즉, 한 트랜잭션이 완료된 후에야 다음 트랜잭션이 시작됩니다. 이는 동시성 문제가 발생하지 않지만, 성능이 떨어질 수 있습니다.
    예: 트랜잭션 T1이 먼저 끝난 후 트랜잭션 T2가 시작되는 경우입니다.

  • 직렬 가능 스케줄(Serializable Schedule):

    여러 트랜잭션이 동시에 실행되지만, 결과적으로 직렬 스케줄과 동일한 결과를 보장하는 스케줄을 말합니다. 즉, 트랜잭션들이 동시에 실행되더라도, 데이터의 일관성을 유지하며, 순차적으로 실행된 것과 같은 결과를 얻습니다.

직렬 스케줄은 직관적이지만 비효율적일 수 있으므로, 직렬 가능 스케줄은 동시성 문제를 해결하면서도 성능을 향상시킬 수 있는 중요한 개념입니다.

2. 직렬 가능 스케줄의 조건

트랜잭션이 동시에 실행될 때, 직렬 가능 스케줄이 되려면 동시성 문제를 피하기 위한 조건들이 있습니다. 대표적으로 충돌 직렬 가능성(Conflict Serializable)뷰 직렬 가능성(View Serializable)이라는 두 가지 기준이 있습니다.

2.1. 충돌 직렬 가능성(Conflict Serializable)

충돌 직렬 가능성은 트랜잭션 간의 연산 순서에 대한 충돌을 고려하여 직렬 가능성을 판단하는 방법입니다. 트랜잭션 간의 충돌은 동일한 데이터 항목에 대해 동시에 접근할 때 발생하며, 읽기와 쓰기 연산 사이에 충돌이 생길 수 있습니다.

  • 충돌(Conflict): 두 트랜잭션이 같은 데이터 항목에 대해 동시에 접근할 때 발생하는 문제입니다. 일반적으로 다음과 같은 상황에서 충돌이 발생합니다:

    하나의 트랜잭션이 읽기(read)를 수행하고 다른 트랜잭션이 쓰기(write)를 수행하는 경우.
    두 트랜잭션이 동시에 쓰기(write)를 수행하는 경우.

이러한 충돌을 피하기 위해, 트랜잭션의 실행 순서를 바꿀 수 있는지 확인하여 충돌 직렬 가능성을 판단합니다.

충돌 직렬 가능성을 판단하는 방법:

  1. 트랜잭션 그래프(Transaction Dependency Graph) 생성:
  • 트랜잭션들 간의 의존 관계를 나타내는 그래프를 그립니다. 각 트랜잭션을 노드로 표시하고, 충돌이 발생하면 화살표로 두 트랜잭션 간의 의존 관계를 표시합니다.
  1. 사이클(Cycle) 여부 확인:
  • 이 그래프에서 사이클이 없으면 직렬 가능 스케줄입니다. 사이클이 있다는 것은 트랜잭션들이 서로 의존하고 있어 직렬 실행이 불가능하다는 뜻입니다.

예시:

T1: read(A), write(A)
T2: read(A), write(A)

여기서 두 트랜잭션 T1과 T2는 같은 데이터 항목 A를 읽고 쓰기 때문에 충돌이 발생합니다. 이 경우, 두 트랜잭션 간의 충돌 그래프를 그리면, 사이클이 발생하지 않으면 직렬 가능성이 있다고 판단할 수 있습니다.

2.2. 뷰 직렬 가능성(View Serializable)
뷰 직렬 가능성(View Serializable)은 트랜잭션이 데이터를 읽고 쓰는 방식을 기준으로 직렬 가능성을 판단합니다. 뷰 직렬 가능성은 충돌 직렬 가능성보다 덜 엄격한 기준을 사용하지만, 여전히 트랜잭션이 동시에 실행되더라도 직렬 실행과 동일한 결과를 보장할 수 있는지 판단합니다.

뷰 직렬 가능성은 다음을 보장해야 합니다:

  1. 초기 읽기(Read): 트랜잭션들이 데이터 항목을 처음 읽을 때, 직렬 실행과 동일한 값을 읽어야 합니다.
  2. 최종 쓰기(Write): 트랜잭션이 완료된 후 데이터 항목에 최종으로 기록된 값은 직렬 실행과 동일해야 합니다.
  3. 읽기 간 의존성: 트랜잭션이 읽은 데이터 값이 다른 트랜잭션이 쓰기 작업을 수행한 결과와 일치해야 합니다.
    뷰 직렬 가능성을 만족하는 스케줄은 직렬 스케줄과 결과가 동일하지만, 모든 충돌을 명시적으로 고려하지는 않습니다.

3. 직렬 가능 스케줄을 왜 사용하나요?

직렬 가능 스케줄은 데이터의 일관성 유지와 성능 최적화 사이의 균형을 맞추기 위해 사용됩니다. 동시성 제어를 하지 않고 직렬로만 트랜잭션을 실행하면, 성능이 크게 저하될 수 있습니다. 반면, 모든 트랜잭션을 동시에 실행하면 동시성 문제가 발생할 수 있습니다.

직렬 가능 스케줄을 사용하면:

  • 성능을 유지하면서도 트랜잭션이 직렬로 실행되는 것과 같은 일관성 있는 결과를 보장할 수 있습니다.
  • 트랜잭션 간의 충돌을 제어하여 데이터 무결성과 일관성을 보장합니다.

4. 직렬 가능 스케줄과 동시성 제어 기법

직렬 가능 스케줄을 구현하기 위해 다양한 동시성 제어 기법이 사용됩니다. 대표적인 기법은 2단계 잠금 규약(2PL, Two-Phase Locking)입니다.

2단계 잠금 규약(2PL)

2PL은 잠금(Lock)을 사용하여 직렬 가능 스케줄을 보장하는 동시성 제어 기법입니다. 트랜잭션이 데이터를 수정하거나 읽을 때, 다른 트랜잭션이 해당 데이터에 접근하지 못하도록 잠금을 겁니다. 2PL은 두 가지 단계로 나뉩니다:

  1. 확장 단계(Growing Phase): 트랜잭션이 잠금을 요청하고, 더 많은 데이터에 잠금을 설정할 수 있는 단계입니다.
  2. 축소 단계(Shrinking Phase): 트랜잭션이 잠금을 해제하는 단계입니다. 이 단계에서는 새로운 잠금을 설정할 수 없습니다.

2PL을 사용하면 트랜잭션 간의 충돌을 방지하고, 직렬 가능성을 보장할 수 있습니다. 하지만 교착 상태(Deadlock)가 발생할 수 있다는 단점도 있습니다.

결론

직렬 가능 스케줄은 트랜잭션 동시성 제어에서 데이터의 일관성을 유지하는 핵심 개념입니다. 여러 트랜잭션이 동시에 실행되더라도, 결과적으로 직렬 스케줄과 동일한 결과를 보장하는 것이 직렬 가능 스케줄의 목표입니다. 이를 위해 충돌 직렬 가능성과 뷰 직렬 가능성 같은 기준을 사용하며, 이를 보장하기 위한 다양한 동시성 제어 기법들이 활용됩니다.


Q1: 충돌 직렬 가능성과 뷰 직렬 가능성의 차이점은 무엇인가요?

충돌 직렬 가능성(Conflict Serializable)뷰 직렬 가능성(View Serializable)은 모두 직렬 가능성을 판단하는 기준이지만, 두 개념에는 차이가 있습니다.

  1. 충돌 직렬 가능성:
  • 트랜잭션 간의 충돌(Conflict)을 기준으로 직렬 가능성을 판단합니다. 충돌은 두 트랜잭션이 같은 데이터를 동시에 읽거나 쓰려고 할 때 발생합니다.
  • 충돌 직렬 가능성은 트랜잭션 간의 충돌을 분석하여 트랜잭션의 실행 순서를 조정할 수 있는지 확인합니다. 이를 위해 트랜잭션 간의 의존성을 나타내는 그래프를 그리고, 사이클이 없는 경우 직렬 가능하다고 판단합니다.
  • 일반적으로 더 엄격한 기준입니다.
  1. 뷰 직렬 가능성:
  • 충돌보다는 트랜잭션이 데이터를 읽고 쓰는 방식을 기준으로 직렬 가능성을 판단합니다. 트랜잭션이 읽고 쓰는 값이 직렬 실행과 동일한지를 확인합니다.
  • 세 가지 주요 조건(초기 읽기, 최종 쓰기, 읽기 간 의존성)을 만족하는지 판단합니다.
  • 뷰 직렬 가능성은 충돌 직렬 가능성보다 덜 엄격하지만, 동일한 결과를 보장할 수 있습니다.

정리하자면, 충돌 직렬 가능성은 더 엄격한 충돌 기반의 분석을 통해 직렬 가능성을 판단하는 반면, 뷰 직렬 가능성은 트랜잭션 간 읽기/쓰기 작업의 결과를 기준으로 판단합니다.

Q2: 직렬 가능 스케줄을 보장하기 위해 2단계 잠금 규약(2PL) 외에 다른 동시성 제어 기법이 있을까요?

네, 2단계 잠금 규약(2PL) 외에도 여러 가지 동시성 제어 기법들이 있습니다. 주요 기법은 다음과 같습니다:

  1. 타임스탬프 순서(Time Stamp Ordering):
  • 각 트랜잭션에 타임스탬프를 부여하고, 트랜잭션이 시작된 순서에 따라 동시성 제어를 합니다. 트랜잭션 간의 충돌이 발생할 경우, 타임스탬프를 기준으로 먼저 시작된 트랜잭션이 우선권을 가지며, 나중에 시작된 트랜잭션은 롤백될 수 있습니다.
  • 이를 통해 충돌 직렬 가능성을 보장할 수 있습니다.
  1. 낙관적 동시성 제어(Optimistic Concurrency Control):
  • 트랜잭션이 충돌 가능성을 낮게 보고, 충돌 검사를 트랜잭션이 완료된 후에 수행합니다. 트랜잭션은 데이터를 수정하기 전에 모든 작업을 먼저 수행하고, 마지막 단계에서 검증 단계를 거칩니다. 검증에서 충돌이 감지되면 트랜잭션을 롤백합니다.
  • 동시성이 높고, 충돌 가능성이 적은 시스템에서 주로 사용됩니다.
  1. 다중 버전 동시성 제어(MVCC, Multi-Version Concurrency Control):
  • 데이터를 수정할 때, 데이터의 여러 버전을 저장하여 동시성을 관리합니다. 각 트랜잭션은 데이터의 특정 버전을 참조하여 읽기 작업을 수행하며, 다른 트랜잭션이 데이터를 수정하더라도 영향을 받지 않습니다.
  • MVCC는 읽기 성능을 향상시킬 수 있지만, 데이터 버전 관리에 추가적인 자원이 필요합니다.
  • 예시로는 PostgreSQL, MySQL(InnoDB) 같은 데이터베이스에서 이 기법을 사용합니다.

이러한 기법들은 2단계 잠금 규약(2PL)과 달리 잠금 없이 또는 더 적은 잠금을 사용해 동시성 문제를 해결하는 방법입니다.

Q3: 직렬 가능 스케줄이 데이터베이스 성능에 미치는 영향은 무엇이며, 성능 최적화를 위해 고려해야 할 요소는 무엇인가요?

직렬 가능 스케줄은 트랜잭션의 데이터 일관성을 보장하는 데 유리하지만, 성능에 영향을 줄 수 있습니다. 특히, 직렬 가능성을 보장하기 위해 트랜잭션 간의 충돌을 방지하려면 잠금을 더 오래 유지하거나 충돌 발생 시 롤백을 수행해야 하기 때문에 성능 저하가 발생할 수 있습니다.

성능을 최적화하기 위해 고려해야 할 요소는 다음과 같습니다:

  1. 잠금 범위 최소화:
  • 트랜잭션이 잠금을 걸어야 하는 자원의 범위를 최소화하여 경쟁을 줄이고, 다른 트랜잭션이 동시에 실행될 수 있도록 합니다. 예를 들어, 행 수준 잠금을 사용하여 동시성을 높일 수 있습니다.
  1. 트랜잭션의 짧은 실행 시간:
  • 트랜잭션이 가능한 한 짧은 시간 동안만 실행되도록 설계합니다. 짧은 트랜잭션은 잠금을 오래 유지하지 않으므로, 동시성을 더 높일 수 있습니다.
  1. 낙관적 동시성 제어 활용:
  • 동시성이 높고 충돌이 드문 경우, 낙관적 동시성 제어 방식을 사용하여 잠금을 최소화하고 성능을 최적화할 수 있습니다. 충돌 검사는 마지막에만 수행되기 때문에, 잠금으로 인한 대기 시간이 감소합니다.
  1. 인덱스 최적화:
  • 인덱스를 최적화하면 트랜잭션이 데이터를 빠르게 조회하고 수정할 수 있으므로, 트랜잭션의 실행 시간을 단축할 수 있습니다. 인덱스는 데이터를 빠르게 검색하게 도와 잠금을 최소화할 수 있습니다.
  1. 데이터베이스 파티셔닝:
  • 데이터를 파티셔닝하여 트랜잭션이 동시에 다른 파티션에서 작업하도록 분산시킬 수 있습니다. 이를 통해 자원에 대한 경합을 줄이고 병렬 처리 성능을 향상시킬 수 있습니다.
  1. 트랜잭션 격리 수준 선택:
  • 너무 높은 격리 수준을 사용할 경우 성능 저하가 발생할 수 있습니다. 데이터 일관성 요구 사항에 맞춰 적절한 격리 수준(예: READ COMMITTED 또는 REPEATABLE READ)을 선택하여 성능을 최적화할 수 있습니다.

결론적으로, 잠금 범위 관리와 트랜잭션 실행 시간 단축이 직렬 가능 스케줄의 성능 최적화의 핵심이며, 낙관적 동시성 제어나 MVCC 같은 기법을 적절히 사용하면 직렬 가능성을 유지하면서도 성능을 개선할 수 있습니다.

profile
백엔드에서 서버엔지니어가 된 사람

0개의 댓글