ZeroBase/CS

데이터베이스 데드락 - 발생 조건과 해결 방법

Red_Horse 2025. 10. 12. 21:58

둘 이상의 트랜잭션이 서로가 잠금을 포기하기를 기다리는 상황(교착상태)을 말합니다.

 

데드락 예시

Transaction A: Lock(X) → Wait for Lock(Y)
Transaction B: Lock(Y) → Wait for Lock(X)

A는 Y를 기다리고, B는 X를 기다리며
서로 영원히 대기하는 교착상태 발생

 

데드락 발생 조건

다음 4가지 조건이 동시에 충족될 때 데드락 발생

 

1. 상호 배제 (Mutual Exclusion)

자원은 한 번에 하나의 트랜잭션만 사용할 수 있습니다.

 
-- 트랜잭션 A가 Row 1을 잠금
-- 트랜잭션 B는 Row 1에 동시 접근 불가

 

2. 점유 및 대기 (Hold and Wait)

트랜잭션이 최소한 하나의 자원을 점유하고 있으면서 다른 트랜잭션이 점유한 자원을 추가로 요구할 때 발생합니다.

T1: 자원 A 보유 → 자원 B 요청
T2: 자원 B 보유 → 자원 A 요청

 

3. 비선점 (No Preemption)

한 트랜잭션이 점유한 자원은 그 트랜잭션이 명시적으로 해제할 때까지 다른 트랜잭션에 의해 강제로 빼앗길 수 없습니다.

T1이 자원을 보유 중
→ T2가 강제로 빼앗을 수 없음
→ T1이 자발적으로 해제할 때까지 대기

 

4. 순환 대기 (Circular Wait)

각 트랜잭션이 순환적으로 다음 트랜잭션에 의해 점유된 자원을 대기하는 상태입니다.

T1 → (T2가 보유한 자원 대기) → T2
T2 → (T3가 보유한 자원 대기) → T3
T3 → (T1이 보유한 자원 대기) → T1

순환 고리 형성

 

데드락 해결 방법

 

1. Timeout (시간 초과)

일정 시간 이후 트랜잭션이 실행되지 않았을 경우 롤백합니다. 직접적으로 탐지하는 것은 아니지만 일정 시간 이후 트랜잭션이 실행되지 않음을 교착상태로 정의해서 탐지하는 방법입니다.

장점

  • 구현이 간단
  • 데드락 상황을 빠르게 해결 가능

단점

  • 시간 설정이 어려움
  • 실제 데드락이 아닌 상황에서도 트랜잭션이 중단될 수 있음
 
-- MySQL 예시
SET innodb_lock_wait_timeout = 50; -- 50초 대기 후 롤백

 

2. Wait-for Graph (대기 그래프)

트랜잭션에 관한 대기 그래프를 만들고 해당 그래프에 사이클이 발생됨을 주기적으로 검사합니다.

Wait-for Graph 예시:
T1 → T2 → T3 → T1  (사이클 발견 → 데드락!)

단점:
대규모 데이터베이스의 경우 모든 트랜잭션의 사이클을
찾아 데드락을 감지하는 것은 코스트가 커서 잘 사용하지 않음

 

데드락 방지 (Prevention)

 

1. 격리 수준 변경

데드락을 방지하기 위해 격리 수준을 조정하는 것을 말합니다.

-- 낮은 격리 수준 사용
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 데드락 위험 감소, 대신 일관성 문제 발생 가능

 

2. 자원 할당 순서 지정

서비스 로직을 구축할 때 자원 할당이 교착상태가 발생하지 않도록 서비스의 논리 구조를 바꾸는 것을 말합니다.

데드락 발생 가능:
T1: Lock(계좌A) → Lock(계좌B)
T2: Lock(계좌B) → Lock(계좌A)

순서 지정으로 방지:
T1: Lock(계좌A) → Lock(계좌B)
T2: Lock(계좌A) → Lock(계좌B)
(항상 A → B 순서로 잠금)

 

3. Wait-Die 방법 (비선점 기법)

타임스탬프를 기반으로 오래된 트랜잭션을 더 우선순위가 높다고 판정하여 이에 따라 대기, 강제 종료 등을 하는 방식을 가진 비선점 기법입니다.

 

Wait (대기)

우선순위가 높은 트랜잭션이 자원을 요청했을 때, 그 자원이 이미 우선순위가 낮은 트랜잭션에 의해 점유되어 있으면, 우선순위가 높은 트랜잭션은 대기 상태로 들어갑니다.

 

Die (종료)

우선순위가 낮은 트랜잭션이 자원을 요청했을 때, 그 자원이 이미 우선순위가 높은 트랜잭션에 의해 점유되어 있으면, 우선순위가 낮은 트랜잭션은 롤백되고, 나중에 다시 시도합니다.

예시 (타임스탬프가 작을수록 오래됨 = 우선순위 높음):
T1 (timestamp: 100) - 우선순위 높음
T2 (timestamp: 200) - 우선순위 낮음

상황 1: T1이 T2가 보유한 자원 요청
→ Wait: T1 대기 (우선순위 높은 트랜잭션이므로)

상황 2: T2가 T1이 보유한 자원 요청
→ Die: T2 롤백 (우선순위 낮은 트랜잭션이므로)

 

4. Wound-Wait 방법 (선점 기법)

타임스탬프를 기반으로 오래된 트랜잭션을 더 우선순위가 높다고 판정하여 이에 따라 대기, 강제 종료 등을 하는 방식을 가진 선점 기법입니다.

 

Wound (부상)

우선순위가 높은 트랜잭션이 자원을 요청했을 때, 그 자원이 이미 우선순위가 낮은 트랜잭션에 의해 점유되어 있으면, 우선순위가 낮은 트랜잭션을 부상시킵니다. 해당 트랜잭션의 점유 자원을 강제 해제하고 우선순위가 높은 트랜잭션에게 넘겨줍니다.

 

Wait (대기)

우선순위가 낮은 트랜잭션이 자원을 요청했을 때, 그 자원이 이미 우선순위가 높은 트랜잭션에 의해 점유되어 있으면, 우선순위가 낮은 트랜잭션은 높은 우선순위를 가진 트랜잭션이 해제될 때까지 기다립니다.

예시:
T1 (timestamp: 100) - 우선순위 높음
T2 (timestamp: 200) - 우선순위 낮음

상황 1: T1이 T2가 보유한 자원 요청
→ Wound: T2 강제 종료, T1에게 자원 넘김 (선점)

상황 2: T2가 T1이 보유한 자원 요청
→ Wait: T2 대기

 

Wait-Die vs Wound-Wait 비교

특성 Wait-Die (비선점) Wound-Wait (선점)
우선순위 높음 → 낮음 Wait (대기) Wound (강제 종료)
우선순위 낮음 → 높음 Die (롤백) Wait (대기)
자원 강제 회수 불가능 가능
롤백 빈도 높음 낮음