데드락
Deadlock
두 개 이상의 프로세스가 서로의 리소스를 기다리며 무한 대기하는 상태. 교착 상태.
Deadlock
두 개 이상의 프로세스가 서로의 리소스를 기다리며 무한 대기하는 상태. 교착 상태.
데드락(Deadlock)은 두 개 이상의 트랜잭션이 서로가 점유한 리소스를 기다리며 영원히 진행되지 못하는 교착 상태입니다. 예를 들어, 트랜잭션 A가 테이블 X를 락한 후 테이블 Y를 요청하고, 트랜잭션 B가 테이블 Y를 락한 후 테이블 X를 요청하면 데드락이 발생합니다.
데드락의 4가지 필수 조건은 상호 배제(한 번에 하나만 사용), 점유 대기(점유하면서 다른 것 요청), 비선점(강제 회수 불가), 순환 대기(원형으로 대기)입니다. 이 중 하나라도 깨면 데드락을 예방할 수 있습니다.
대부분의 DBMS는 데드락을 자동 감지하고, 비용이 낮은 트랜잭션을 롤백시켜 해결합니다. 애플리케이션에서는 재시도 로직을 구현해야 합니다.
-- 데드락 발생 시나리오
-- 트랜잭션 A
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- A가 row 1 락
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- B의 락 대기
-- 트랜잭션 B (동시 실행)
BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 2; -- B가 row 2 락
UPDATE accounts SET balance = balance + 50 WHERE id = 1; -- A의 락 대기 → DEADLOCK!
-- 해결책: 일관된 순서로 락 획득
-- 항상 id가 작은 것부터 처리
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- Python에서 재시도 로직
import time
from sqlalchemy.exc import OperationalError
def transfer_with_retry(from_id, to_id, amount, max_retries=3):
for attempt in range(max_retries):
try:
with db.begin():
# 일관된 순서로 락 획득
ids = sorted([from_id, to_id])
db.execute(f"SELECT * FROM accounts WHERE id IN ({ids}) FOR UPDATE")
db.execute(f"UPDATE accounts SET balance = balance - {amount} WHERE id = {from_id}")
db.execute(f"UPDATE accounts SET balance = balance + {amount} WHERE id = {to_id}")
return True
except OperationalError as e:
if "deadlock" in str(e).lower() and attempt < max_retries - 1:
time.sleep(0.1 * (2 ** attempt)) # 지수 백오프
continue
raise
운영팀: "결제 시스템에서 간헐적으로 deadlock 에러가 발생해요."
DBA: "SHOW ENGINE INNODB STATUS 로 확인해보니 포인트 차감이랑 잔액 업데이트가 서로 순서가 다르네요."
백엔드 개발자: "동시 결제 시 락 순서가 꼬이는 거군요. 항상 user_id 순으로 락 잡도록 수정할게요."
DBA: "그리고 트랜잭션 범위도 줄여보세요. 외부 API 호출은 트랜잭션 밖으로 빼시고요."
면접관: "데드락이 발생하는 조건과 예방 방법을 설명해주세요."
지원자: "상호 배제, 점유 대기, 비선점, 순환 대기 네 가지 조건이 동시에 만족될 때 발생합니다. 예방을 위해 리소스 획득 순서를 일관되게 하고, 트랜잭션 범위를 최소화하고, 타임아웃을 설정합니다."
면접관: "감지 후 해결은요?"
지원자: "DBMS가 자동 감지하고 하나를 롤백하면 애플리케이션에서 지수 백오프로 재시도합니다."
리뷰어: "이 코드 보면 먼저 주문 테이블 업데이트하고 재고 테이블 업데이트하는데..."
개발자: "네, 주문 생성 로직이에요."
리뷰어: "근데 취소 로직에서는 재고 먼저 복구하고 주문 상태 바꾸네요. 동시 실행되면 데드락 가능성 있어요."
개발자: "아, 락 순서를 맞춰야겠네요. 취소도 주문→재고 순으로 수정할게요."