혹시 '데이터 베이스를' 아십니까 ? #5 – 교착상태(Deadlock)

전하윤·2025년 7월 7일
0

DB

목록 보기
5/7
post-thumbnail

목차


개요

앞선 글 마지막에서 트랜잭션 관리 시 주의사항과 함께
"두 개 이상의 트랜잭션이 서로 상대방의 락 해제를 기다리며
무한정 대기에 빠지는 현상"인 데드락(Deadlock)을 간략히 소개했었다.

실제로 데이터베이스를 운영하다 보면
단순히 이론으로만 접했던 데드락이
실제 장애의 원인으로 자주 등장한다는 사실을 경험하게 됩니다.

특히 여러 사용자가 동시에 데이터를 수정하거나,
여러 트랜잭션이 복잡하게 엮이는 실시간 환경에서는
데드락이 발생할 가능성이 더욱 높아집니다.

이번 글에서는
데드락이란 정확히 무엇이며,
왜 발생하고, 어떤 식으로 실무에서 문제를 일으키는지

실제 SQL 실습 예시통해 직접 실험 해봤다.

앞선 이론적 설명에서 한걸음 더 나아가
"실제로 마주치는 데드락 장애를 어떻게 다룰 것인가?"에 집중합니다.


데드락(Deadlock)이란?

데드락 1초만에 이해가능
  • 데드락은 둘 이상의 트랜잭션이 서로 상대방이 점유한 자원을 기다리면서 무한 대기 상태에 빠지는 현상입니다.
  • 즉, 각 트랜잭션이 서로의 ‘락 해제’를 기다리기 때문에, 아무도 작업을 진행하지 못합니다.
  • DBMS는 보통 한쪽 트랜잭션을 강제로 롤백시켜 해결하지만,
    빈번하게 발생하면 전체 서비스에 큰 영향을 미칠 수 있습니다.

왜 데드락이 발생하는가?

  • 동시에 여러 트랜잭션이 데이터의 행 또는 테이블에 락을 걸고,
    각각 자신이 필요한 락을 점유한 채 상대방이 점유한 락을 추가로 기다리면서 발생합니다.
  • 예를 들어, 트랜잭션1이 A를 점유 후 B를 기다리고, 트랜잭션2가 B를 점유 후 A를 기다리는 구조에서 쉽게 발생합니다.

데드락을 실전에 비유

🏢 회의실 열쇠 비유

  • 두 명의 팀원이 각각 회의실1, 회의실2 열쇠를 먼저 들고,
    동시에 상대방이 가진 열쇠까지 필요해서 요청하면 둘 다 열쇠를 서로 줄 때까지 ‘무한정 대기’에 빠지는 상황입니다.
  • 현실에서도 “서로가 끝내주기를 기다리며 아무도 진행하지 못하는” 회의/개발/업무 상황에 자주 비유됩니다.

어떻게 해결해야 하는가?

  • DBMS는 데드락이 감지되면 한쪽 트랜잭션을 강제 롤백(Rollback) 시켜서
    더 이상 무한 대기가 이어지지 않게 만듭니다.
  • 하지만 데드락이 잦으면 서비스 응답이 느려지거나 결제/입금 등 핵심 비즈니스 로직이 실패할 수 있으므로,
    가능한 한 원인을 줄이거나, 설계 단계에서 예방하는 것이 중요합니다.

실제 SQL 예시 (데드락 실패)

아래 쿼리는 MySQL 등에서 두 개의 세션을 이용해 직접 데드락 상황을 유도하는 기본 예시입니다.

참고로 데드락 상황은 2개의 서로 다른 "세션"(=연결창, 터미널, 쿼리 실행창)에서 발생시켜야 합니다.

1. 실습용 테이블 생성 및 데이터 삽입

2. 첫 번째 세션

  • id=1 행에 먼저 락을 획득하고 트랜잭션을 유지

  • 이후 다른 세션으로 id 2행에 대해 락을 획득하고 트랜잭션을 유지

3. 두 번째 세션

  • 동시에 id=2 행에 대해 락을 획득하고 트랜잭션을 유지

  • 이렇게 되면 첫 번째 세션이 id=2에, 두 번째 세션이 id=1에 상호 락 요청
    두 세션 모두 서로의 락이 해제되길 무한 대기

  • 이후 id= 2를 점유한 프로시저가 id = 1의 데이터를 요청할 시에

  • MySQL 등 대부분의 DBMS는 데드락을 감지하면,
    둘 중 한 쪽 트랜잭션을 강제로 롤백하여 교착상태를 해소함

  • 에러 코드

20:37:26	UPDATE deadlock_test SET value = value + 100 WHERE id = 2	Error Code: 2013. Lost connection to MySQL server during query	30.001 sec

위의 상황에서 두 세션이 서로 락을 점유한 채 서로가 해제하기만 기다리므로,
DBMS가 자동으로 둘 중 하나를 롤백시켜 데드락을 해소합니다.


실제 SQL 예시 (성공 사례)

트랜잭션의 락 점유 순서를 동일하게 맞추면 데드락이 발생하지 않습니다.

1. 첫 번째 세션

  • 먼저 id=1에 락을 건 뒤, id=2에 락을 걸고 모두 끝나면 COMMIT

2. 두 번째 세션 (첫 번째 세션이 끝난 후 시작)

  • SESSION 1이 완료된 후 같은 순서(id=1 → id=2)로 락을 걸고 COMMIT
  • 모든 트랜잭션이 동일한 순서로만 자원을 점유하면,
    DBMS가 순차적으로 락 점유를 대기하게 만들어 데드락이 발생하지 않게 처리해준다.

핵심: 여러 트랜잭션이 동일한 자원에 접근할 때
“항상 같은 순서로 락을 걸면” 데드락이 발생하지 않습니다.


데드락 회피 방법은 없는가?

1. 타임스탬프 기반 방식

  • 트랜잭션이 시작될 때 OS 시각 또는 DB 내부 증가값(논리적 시간)으로 순번을 부여해 관리한다.
  • MySQL 등에서는 실제로도 내부적으로 Transaction ID(trx_id) 등을 사용해 트랜잭션을 구분한다.

2. Wait-Die / Wound-Wait 기법

Wait-Die (비선점)

  • 먼저 시작한 트랜잭션(번호가 낮음)은 기다릴 수 있음.
  • 나중에 시작한 트랜잭션(번호가 높음)은 바로 롤백됨.

Wound-Wait (선점)

  • 먼저 시작한 트랜잭션은 무조건 기다리지 않고, 나중에 시작한 트랜잭션이 락을 잡고 있으면 바로 롤백시킴.

이 기법들은 DBMS 내부에서 자동 구현이 되어 있다.


데드락 빈도를 낮추는 방법

1. 트랜잭션을 자주, 빠르게 커밋하라

  • 트랜잭션을 오래 열어두면 락이 계속 유지되어 데드락 확률이 올라감.

  • 각 작업(UPDATE, INSERT, DELETE)마다 바로 커밋하는 습관이 중요하다.

  • START TRANSACTION
    UPDATE accounts SET balance = balance - 100 WHERE id = 1
    COMMIT```

2. 접근 순서를 항상 ‘일관’되게

  • 모든 트랜잭션이 동일한 테이블/행에 항상 같은 순서(예: 오름차순)로 접근해야 한다.

  • 순서가 꼬이면 "서로 락을 잡고 대기"하는 데드락이 발생할 수 있다.

  • SELECT * FROM accounts WHERE id IN (1, 2) ORDER BY id FOR UPDATE```

3. 여러 행을 랜덤하게 갱신해야 한다면, 테이블 락 사용

  • 여러 row를 한 번에 업데이트할 때 WHERE 조건에 따라 순서가 예측 불가하면,
    테이블 단위로 락을 걸고(LOCK TABLES) 처리하면 데드락을 예방할 수 있다.

  • 단, 동시성은 다소 떨어질 수 있으니 꼭 필요한 경우에만 적용.

  • LOCK TABLES items WRITE
    UPDATE items SET stock = stock - 1 WHERE id IN (3, 8, 15)
    UNLOCK TABLES```

💡 정리

  • 락 순서 고정, 트랜잭션 시간 최소화, 빠른 커밋, 필요한 부분만 락, 인덱스 적극 활용 등으로 데드락은 줄이고, 시스템 안정성은 높일 수 있다.
  • 위 전략만 잘 실천해도 대부분의 실무 데드락 위험은 충분히 예방할 수 있다고 합니다.

참고:
대규모 시스템에서는 (SET innodb_lock_wait_timeout = 5) 와 같은 옵션으로
트랜잭션 대기 시간을 제한하고, 자동 롤백을 설정할 수도 있습니다.


Reference

[MySQL] Deadlock의 원인과 예방 방법
Preventing deadlock with timestamps: wound-wait scheme

profile
개발에 대한 고민과 성장의 기록을 일기장처럼 성찰하며 남기는 공간

1개의 댓글

comment-user-thumbnail
2025년 7월 7일

좋은 글 감사합니다

답글 달기