데이터베이스 따라잡기

데드락과 타임아웃

meellon 2026. 6. 28. 05:30

학습 목표

데드락순환 대기로 발생함을 설명할 수 있다.

대기 그래프(wait-for graph) 로 교착 사이클을 찾는 방법을 설명할 수 있다.

감지·예방·회피lock timeout·NOWAIT 차이를 구분할 수 있다.

애플리케이션에서 데드락·직렬화 실패재시도 패턴을 설계할 수 있다.

문제 상황

  • 주문 A·B가 동시에 서로의 행FOR UPDATE — 둘 다 무한 대기
    • API는 30초 후 504, DB 세션은 락을 쥔 채 남음
  • 재고 차감·포인트 차감을 순서 없이 두 테이블에 락 — 가끔 deadlock detected
    • 같은 코드인데 부하 올리면 재현
  • lock_timeout 없이 긴 txn — 슬로우 쿼리 하나가 전체 결제를 막음
  • ORM @Transactional 안에서 외부 HTTP 호출
    • 오래 보유 → 교착·대기 확률 상승
  • MySQL 1213, PostgreSQL 40P01어느 txn을 롤백할지 앱이 처리해야 함

앞서 격리 수준·2PL·FOR UPDATE로 동시성을 제어했다. 락이 많아지면 서로 기다리는 고리가 생긴다. 이번 편은 교착얼마나 기다릴지다.

1. 데드락이란

두 개 이상 txn이 서로가 가진 자원을 기다리며 영원히 진행하지 못하는 상태.

Coffman 조건 — 네 가지가 동시에 성립하면 데드락 가능:

조건 의미
Mutual exclusion 자원을 동시에 한 txn만 사용
Hold and wait 잡은 락을 놓지 않고 다른 락 대기
No preemption DBMS가 락을 강제로 빼앗지 않음 (일반적)
Circular wait T1→T2→…→T1 순환 대기
T1: lock row A
T2: lock row B
T1: wait for row B   ← T2가 보유
T2: wait for row A   ← T1이 보유   → cycle
  • 2PL·비관적 락·Serializable일수록 락 경합↑ → 데드락 빈도
  • MVCC 읽기는 보통 블로킹 없음쓰기·범위 락에서 주로 발생

2. 대기 그래프

DBMS는 누가 누구를 기다리는지 그래프로 추적한다.

요소 의미
노드 활성 txn (또는 자원 — 구현마다 다름)
간선 T1 → T2 T1이 T2가 보유한 락을 기다림
사이클 데드락 — 감지 알고리즘의 핵심
txn 보유 대기
T1 row A (X) row B
T2 row B (X) row A
  • 주기적으로(또는 대기 발생 시) DFS로 사이클 탐색
  • 희생자(victim) 하나 ROLLBACK — 보통 비용 적은 txn (짧은 txn, 적은 작업량)

3. 감지 · 예방 · 회피

감지 (Detection) — PostgreSQL·InnoDB 기본

  • 대기 중 주기적 사이클 검사
  • 발견 시 victim 롤백 — 나머지 txn 진행
  • 오버헤드는 있지만 동시성 유지
DB 에러 의미
PostgreSQL 40P01 deadlock_detected victim txn 전체 abort
MySQL InnoDB 1213 Deadlock found victim 롤백

예방 (Prevention)

방법 설명
락 순서 통일 항상 (user_id 작은 쪽)(큰 쪽) 순으로 FOR UPDATE
한 번에 잠금 SELECT ... WHERE id IN (1,2) ORDER BY id FOR UPDATE
락 범위 축소 txn 짧게, 필요한 행만
  • 애플리케이션 설계로 순환 자체를 없애는 접근

회피 (Avoidance)

방법 설명
lock timeout SET lock_timeout = '2s' — 초과 시 에러·대기 중단
NOWAIT FOR UPDATE NOWAIT — 대기 없이 즉시 실패
SKIP LOCKED 큐·워커 — 잠긴 행 건너뜀
  • 데드락을 기다리지 않음 — 앱이 재시도·다른 행 처리

4. lock timeout vs statement timeout

설정 PostgreSQL 효과
lock_timeout SET lock_timeout = '3s' 락 대기 상한 — 초과 시 취소
statement_timeout SET statement_timeout = '30s' 쿼리 전체 실행 상한
idle_in_transaction_session_timeout 세션 설정 txn 열린 채 idle 상한
-- session-level (PostgreSQL)
SET lock_timeout = '2s';
SET statement_timeout = '30s';

BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;  -- waits max 2s for lock
-- ...
COMMIT;
-- no wait
SELECT * FROM seats WHERE id = 42 FOR UPDATE NOWAIT;

-- queue worker: skip locked rows
SELECT * FROM jobs
WHERE status = 'PENDING'
ORDER BY id
FOR UPDATE SKIP LOCKED
LIMIT 1;
  lock_timeout statement_timeout
측정 기다린 시간 실행 전체 시간
용도 교착·장기 대기 차단 슬로우 쿼리·runaway 차단
데드락 직접 해결 안 함 — 포기 후 재시도 victim 에도 긴 대기 가능
  • 둘 다 설정하는 경우 많음 — lock_timeoutstatement_timeout 권장

5. 애플리케이션 재시도

DB가 victim을 롤백해도 클라이언트에러를 받는다. 멱등·재시도가 필요하다.

단계 처리
1 txn 시작 · 비즈니스 로직
2 deadlock / lock timeout / serialization_failure
3 ROLLBACK (자동 또는 명시)
4 지수 백오프 + 재시도 (상한 N회)
5 초과 시 사용자에게 실패
-- PostgreSQL: serialization failure (SSI)
-- SQLSTATE 40001
재시도 대상 SQLSTATE (PG) 비고
Deadlock 40P01 victim txn
Lock timeout 55P03 lock_not_available lock_timeout
Serialization failure 40001 Serializable 충돌
// Spring — declarative retry (concept)
@Retryable(
    retryFor = { PSQLException.class, CannotAcquireLockException.class },
    maxAttempts = 3,
    backoff = @Backoff(delay = 50, multiplier = 2)
)
@Transactional
public void transfer(Long from, Long to, BigDecimal amount) { ... }
  • 멱등 키 — 재시도 시 이중 출금 방지
  • 락 순서 — 재시도만으로 근본 해결 안 됨 — 설계 병행

6. 실무 체크리스트

항목 권장
다중 테이블 락 PK·비즈니스 키 기준 정렬 후 잠금
txn 길이 DB 작업만 — HTTP·메시지 밖으로
모니터링 pg_locks, innodb_locks, deadlock 로그
기본값 lock_timeout 설정 (0=무한 대기 주의)
Serializable 재시도 필수 — 40001 처리
워커 큐 SKIP LOCKED경쟁 consumer
-- PostgreSQL: who blocks whom
SELECT blocked.pid     AS blocked_pid,
       blocking.pid    AS blocking_pid,
       left(blocked.query, 60)  AS blocked_query,
       left(blocking.query, 60) AS blocking_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
  ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0;

7. 정리

  • 데드락 = 순환 대기 — Coffman 네 조건
  • 대기 그래프 사이클 → victim 롤백 (감지)
  • 예방 — 락 순서·txn 짧게·범위 최소
  • 회피lock_timeout, NOWAIT, SKIP LOCKED
  • lock_timeoutstatement_timeout
  • 재시도 + 멱등40P01·40001·lock timeout

다음에 다룰 것

  • WAL과 crash recovery
  • redo/undo, checkpoint

해당 내용은 Database System Concepts, 7/E (Avraham Silberschatz, Henry F. Korth, S. Sudarshan) 의 내용을 기반으로 합니다.

'데이터베이스 따라잡기' 카테고리의 다른 글

MVCC  (0) 2026.06.30
로깅과 복구  (0) 2026.06.29
동시성 제어  (0) 2026.06.27
트랜잭션과 ACID  (0) 2026.06.26
실행 계획 읽기  (0) 2026.06.25