변수의 기록

(데이터베이스) 트랜잭션 스케줄링과 Serializability 정리 본문

CS지식/데이터베이스 (Database)

(데이터베이스) 트랜잭션 스케줄링과 Serializability 정리

불광동 물주먹 2025. 5. 19. 22:09

트랜잭션이 동시에 실행될 때, 우리는 무엇을 보장해야 할까?

데이터베이스에서 여러 사용자가 동시에 트랜잭션을 수행한다면 어떤 일이 벌어질까요?

예를 들어, 두 명의 사용자가 동시에 같은 은행 계좌에 접근해 금액을 업데이트한다고 가정해봅시다.

T1: 1000원을 출금
T2: 2000원을 입금

정상이라면 결과는 잔고 +1000원이 되어야겠지만, 두 트랜잭션이 얽혀 실행되면 예기치 못한 결과가 발생할 수 있습니다. 이처럼 트랜잭션의 실행 순서는 단순히 성능을 넘어서 데이터의 정확성과 직결됩니다.


스케줄(Schedule)이란?

스케줄은 여러 트랜잭션이 동시에 실행될 때, 각 연산(읽기, 쓰기)이 실제로 어떤 순서로 실행되었는지를 기록한 것입니다.

중요한 점은:

  • 각 트랜잭션 내부의 연산 순서는 바뀌지 않지만
  • 여러 트랜잭션이 뒤섞여 실행될 수 있다는 점입니다.

그렇다면 어떤 스케줄이 안전할까?

1. Serial Schedule

가장 단순하고 안전한 방식은 트랜잭션을 순차적으로 한 개씩 실행하는 것입니다.

T1: 모든 연산 → 완료 후  
T2: 모든 연산 → 실행

 

이 방식은 일관성은 보장되지만, 성능은 낮습니다.
실제로는 거의 사용되지 않습니다.


2. Non-Serial Schedule

현실 세계에서는 성능을 위해 여러 트랜잭션을 겹쳐서 동시에 실행합니다.

T1: 일부 연산  
T2: 일부 연산  
T1: 나머지 연산  
T2: 나머지 연산

이 방식은 성능은 좋지만, 의도치 않은 충돌이 발생할 수 있습니다.
대표적인 문제가 바로 아래의 Lost Update입니다.


Lost Update: 덮어쓰기 사고

T1: read A (값 10) → A = A + 5write A (15)
T2: read A (값 10) → A = A + 10write A (20)

결과적으로 **T1의 변경사항(15)**은 완전히 사라지고, A는 20이 되어버립니다.
두 트랜잭션 모두 A를 읽고 수정했지만, 마지막에 쓰기(write)한 쪽만 남는 겁니다.

이걸 막기 위해 우리는 스케줄이 안전한지를 판단하는 기준이 필요합니다.


스케줄이 안전하다는 기준: Serializability

Serializability는 "이 스케줄이 어떤 순차적 실행(Serial Schedule)과 동일한 결과를 낸다면 안전하다"는 개념입니다.

즉, 겹쳐 실행은 하되, 결과는 마치 순차적으로 실행한 것처럼 보이게 하자.


어떻게 동일함을 판단할까? (Conflict Serializability)

Conflict란?

두 연산이 다음 조건을 모두 만족하면 **충돌(conflict)**입니다:

  1. 서로 다른 트랜잭션에 속하고
  2. 같은 데이터 항목을 접근하며
  3. 최소 하나는 write 연산이다.

Conflict Equivalent

  • 두 스케줄이 같은 트랜잭션들로 구성되고
  • 모든 충돌 연산들의 실행 순서가 동일하다면
    → 이 둘은 conflict equivalent하다고 합니다.

Conflict-Serializable

  • 어떤 serial schedule과 conflict equivalent하다면
    → 그 스케줄은 conflict-serializable하다고 부릅니다.

이 기준은 단순하고 계산이 가능해서, 실제로 많은 DBMS가 사용하는 기준입니다.

 

Conflict Serializability 예제

두 개의 트랜잭션

T1: R1(A)  W1(A)  R1(B)  W1(B)
T2: R2(A)         W2(A)
  • T1: A를 읽고 쓰고, B를 읽고 씀
  • T2: A를 읽고 씀

📌 예제 스케줄 S1 (Non-Serial Schedule)

S1: R1(A) R2(A) W1(A) W2(A) R1(B) W1(B)

🔍 1. 충돌 연산 확인

  1. R1(A) vs W2(A) → conflict (T1 vs T2, 같은 A, W 포함)
  2. R2(A) vs W1(A) → conflict
  3. W1(A) vs W2(A) → conflict

→ 이들 연산 순서 확인 필요


🔍 2. conflict 연산 순서 (S1)

  • R1(A) → W2(A)
  • R2(A) → W1(A)
  • W1(A) → W2(A)

충돌 순서들:

  • T1 → T2 (R1(A) → W2(A))
  • T2 → T1 (R2(A) → W1(A))
  • T1 → T2 (W1(A) → W2(A))

→ 순환 발생: T1 → T2 → T1
→ conflict-serializable 하지 않다!


📌 예제 스케줄 S2 (Conflict-Serializable한 스케줄)

S2: R1(A) W1(A) R1(B) W1(B) R2(A) W2(A)

🔍 1. 충돌 연산 확인

  • W1(A) vs R2(A) → conflict (T1 먼저)
  • W1(A) vs W2(A) → conflict (T1 먼저)

→ T1의 모든 conflict write 연산이 T2보다 먼저 실행

T1 → T2 순서로 정렬됨 → 순환 없음

✅ S2는 conflict-serializable하다

→ T1 → T2 순차 실행과 동일한 결과
→ 이 스케줄은 Serial Schedule T1 → T2와 conflict equivalent함

요약

 

항목 설명
Conflict 조건 서로 다른 트랜잭션, 같은 데이터, 최소 하나 write
Conflict-Equivalent 충돌 순서가 동일하면 equivalent
Conflict-Serializable 어떤 시리얼 스케줄과 conflict-equivalent하면 OK
충돌 순서에 순환 발생 시 Conflict-Serializable 하지 않음

 


동시성 제어(Concurrency Control)의 목표

우리는 현실에서 트랜잭션을 동시에 실행시켜야 합니다.
하지만 동시에 실행하면서도 결과가 serializable하게 나와야 하죠.

그래서 DBMS는 다양한 동시성 제어 알고리즘을 사용합니다:

  • 2-Phase Locking (2PL)
  • Timestamp Ordering
  • Optimistic Concurrency Control

이들 알고리즘은 각각의 방식으로 conflict-serializable한 스케줄을 보장합니다.


Isolation: 완벽하게 격리하고 싶지만…

Isolation은 트랜잭션이 마치 독립적으로 실행되는 것처럼 보장하는 속성입니다.
하지만 완벽한 격리는 성능에 큰 부담을 줍니다.

그래서 현실적인 절충안으로 Isolation Level이라는 개념이 등장합니다.


 

격리 수준 설명 발생 가능 문제
Read Uncommitted 커밋되지 않은 데이터도 읽음 Dirty Read, Non-repeatable Read
Read Committed 커밋된 데이터만 읽음 Non-repeatable Read, Phantom Read
Repeatable Read 같은 데이터를 반복 조회해도 결과 동일 Phantom Read 가능
Serializable 가장 높은 수준, serial schedule과 동일 성능 저하 가능, 교착 상태 발생 가능성
 

Isolation Level은 DBMS 설정, JDBC, 혹은 Spring에서 설정 가능합니다.


정리

  • 스케줄은 트랜잭션들이 어떤 순서로 실행되었는지를 보여주는 흐름이다.
  • 시리얼 스케줄은 안전하지만 현실적이지 않고, non-serial은 현실적이지만 위험하다.
  • serializability는 이러한 딜레마를 해결하는 이론적 기반이다.
  • 특히 conflict-serializability는 실제 구현 가능한 기준이며, 많은 DBMS가 이 원칙을 기반으로 동작한다.
  • 동시성 제어는 성능을 유지하면서도 데이터 정합성을 지키기 위한 핵심 기술이다.
  • Isolation Level은 이 모든 것을 성능과 안전성 사이에서 조율하는 수단이다.

 

 

 

다음 추천 강의

https://moon-97.tistory.com/94