Foundry
데이터베이스
중급
핵심

데드락

트랜잭션 간 순환 대기, 예방 전략

데드락 (Deadlock)

핵심 개념

두 트랜잭션이 서로의 락을 기다리며 영원히 멈추는 상태.

[데드락 발생 시나리오]

TX1: LOCK(A) → 대기(B)...
TX2: LOCK(B) → 대기(A)...

TX1 ──락──→ [A] ←──대기── TX2
TX1 ──대기──→ [B] ←──락── TX2

→ 서로 상대방의 락을 기다림
→ 영원히 진행 불가!

데드락 4가지 조건 (모두 충족 시 발생)

조건설명
상호 배제자원은 한 번에 하나만 사용
점유 대기자원 보유 + 추가 자원 대기
비선점강제로 빼앗을 수 없음
순환 대기A→B→A 순환 형태

4가지 중 하나라도 깨면 데드락 방지

DB에서의 데드락

-- TX1
BEGIN;
UPDATE accounts SET balance = balance - 100
  WHERE id = 1;  -- id=1 락
UPDATE accounts SET balance = balance + 100
  WHERE id = 2;  -- id=2 대기...

-- TX2 (동시에)
BEGIN;
UPDATE accounts SET balance = balance - 50
  WHERE id = 2;  -- id=2 락
UPDATE accounts SET balance = balance + 50
  WHERE id = 1;  -- id=1 대기...

→ 데드락!

해결 방법

1. 락 순서 통일
   항상 id 작은 것부터 → 순환 대기 방지
   TX1: LOCK(1) → LOCK(2)
   TX2: LOCK(1) → LOCK(2)  ← 같은 순서!

2. 타임아웃 설정
   SET innodb_lock_wait_timeout = 5;
   → 5초 후 자동 롤백

3. DB 자동 감지
   MySQL InnoDB: 데드락 감지 → 한쪽 롤백
   → 비용 적은 TX를 희생 (victim)

실무 예방 패턴

권장: 트랜잭션 짧게 유지
권장: 락 순서 일관되게
권장: 인덱스 사용 (락 범위 최소화)
권장: 적절한 격리 수준 선택
피할 것: 트랜잭션 안에서 외부 API 호출
피할 것: 불필요하게 넓은 범위 락
면접에서 이렇게 나옵니다

Q.데드락이 발생하는 4가지 조건은 무엇인가요?

넷이 모두 성립해야 데드락이 됩니다. 하나만 깨면 예방됩니다.

조건깨는 방법
상호 배제한 자원을 한 번에 하나만 쓴다대개 깰 수 없다. 잠금의 존재 이유다
점유 대기하나를 쥔 채 다른 것을 기다린다필요한 것을 한 번에 모두 잡는다
비선점남의 것을 뺏을 수 없다타임아웃을 두어 강제 해제한다
순환 대기서로가 서로를 기다리는 고리잠금 순서를 고정한다

실무에서 가장 현실적인 것은 순환 대기 제거입니다. 계좌 번호가 작은 쪽부터 잠그는 식으로 순서를 정하면 고리가 생기지 않습니다.

상호 배제를 깨는 것은 대개 불가능합니다. 잠금이 없으면 데이터 무결성이 깨지기 때문입니다.

흔한 실수: 네 조건을 외우고 끝내는 것. 어느 것을 깰 수 있고 어느 것은 못 깨는지, 그리고 왜 순서 고정이 실무 답인지가 요지입니다.

Q.DB에서 데드락을 예방하는 방법을 설명해주세요

방법내용
잠금 순서 고정여러 행을 잠글 때 항상 같은 기준(예: id 오름차순)으로
트랜잭션을 짧게잠금을 쥐는 시간을 줄인다
트랜잭션 안에서 외부 호출 금지결제사 호출 중에 잠금을 쥐고 있으면 대기가 길어진다
인덱스 정비인덱스가 없으면 넓은 범위를 잠가 경합이 커진다
격리 수준 조정필요 이상으로 높으면 갭 잠금 등으로 범위가 넓어진다

예방만으로는 0이 되지 않습니다. 잠글 대상을 미리 알 수 없는 경우, 인덱스 범위나 외래키처럼 코드에 안 보이는 잠금, 배치나 관리자 화면 같은 다른 경로가 남습니다.

그래서 재시도가 함께 가야 합니다. 데드락으로 롤백된 트랜잭션은 전체가 취소됐으므로 다시 시도하는 것이 안전합니다.

흔한 실수: 예방을 완벽히 하면 재시도가 필요 없다고 보는 것. 그렇게 믿는 순간 드물게 실패하는 기능이 남습니다.

Q.MySQL InnoDB는 데드락을 어떻게 감지하고 처리하나요?

대기 그래프를 만들어 고리를 찾으면 한쪽을 롤백합니다.

단계내용
감지어떤 트랜잭션이 어떤 잠금을 기다리는지 그래프로 관리한다
판정고리가 생기면 데드락
희생자 선택되돌릴 양이 적은 쪽을 고른다
처리그 트랜잭션을 롤백하고 오류를 반환한다

애플리케이션은 그 오류를 받아 재시도해야 합니다. 이 처리를 안 하면 사용자에게 알 수 없는 오류로 보입니다.

감지를 끄고 타임아웃으로만 처리하는 설정도 있습니다. 동시성이 아주 높으면 감지 자체가 비용이라 그렇게 두기도 하지만, 그러면 데드락이 타임아웃까지 대기로 남습니다.

최근 데드락 내역은 엔진 상태 출력에서 확인할 수 있어, 어떤 두 쿼리가 부딪혔는지 볼 수 있습니다.

흔한 실수: 감지되면 DB 가 알아서 해결한다고 답하는 것. DB 는 한쪽을 죽일 뿐이고, 살리는 것은 애플리케이션의 재시도입니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

데이터베이스 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.