VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
ERROR 1213: Deadlock found when trying to get lock; try restarting transaction과 함께 종료합니다.
교착 상태가 발생하는 방식
전형적인 패턴: 두 트랜잭션이 행을 반대 순서로 잠급니다.교착 상태 로그 읽기
가장 최근의 교착 상태는 InnoDB 상태 출력에서 항상 볼 수 있습니다:LATEST DETECTED DEADLOCK 절을 찾으세요. 두 트랜잭션, 각각이 보유한 잠금, 기다리던 잠금, 어느 트랜잭션이 롤백되었는지를 보여 줍니다.
- 어떤 두 쿼리가 충돌하는지
- 어떤 테이블과 인덱스가 관련되어 있는지
- 어느 트랜잭션이 희생자(롤백됨)였는지
교착 상태 예방 패턴
일관된 순서로 행 잠그기
위의 교착 상태는 A가 행 1 다음 행 2를 잠그고, B가 행 2 다음 행 1을 잠그기 때문에 발생합니다. 두 트랜잭션이 항상 ID 오름차순으로 잠그면 순환이 형성될 수 없습니다:트랜잭션을 짧게 유지하기
긴 트랜잭션은 잠금을 더 오래 보유하여 충돌이 생길 시간을 더 줍니다. 트랜잭션 안에서는 최소한의 작업만 하세요:SELECT … FOR UPDATE로 미리 잠그기
트랜잭션이 행을 갱신할 예정이라면, UPDATE에서 잠금 충돌을 발견하기보다 SELECT에서 잠그세요:교착 상태 시 재시도
교착 상태는 동시성 시스템에서 예상되는 일입니다. 올바른 애플리케이션 대응은ERROR 1213을 잡아 트랜잭션 전체를 재시도하는 것입니다:
갭 잠금과 넥스트 키 잠금
InnoDB는 기본REPEATABLE READ 격리 수준에서 팬텀 읽기를 막기 위해 갭 잠금과 넥스트 키 잠금을 사용합니다. 이 더 넓은 잠금은 교착 상태 가능성을 높입니다.
READ COMMITTED는 행 잠금만 사용하므로(갭 잠금 없음) 교착 상태 빈도가 줄어듭니다. 트레이드오프는 팬텀 읽기를 허용한다는 것입니다. 대부분의 OLTP 워크로드에서 교착 상태에 민감한 작업에는 READ COMMITTED가 합리적인 선택입니다.
잠금 대기 타임아웃
모든 잠금 충돌이 교착 상태는 아닙니다. 트랜잭션 B가 단순히 A의 잠금을 기다리고 A가 느리다면, B는innodb_lock_wait_timeout초(기본값: 50) 후에 타임아웃됩니다. 오류는 ERROR 1205: Lock wait timeout exceeded입니다.
자주 묻는 질문
현재 모든 잠금과 누가 기다리는지 어떻게 보나요?
data_locks는 현재 보유되거나 요청된 모든 잠금을 보여 줍니다. data_lock_waits는 어떤 스레드가 어떤 스레드를 차단하는지 보여 줍니다.
InnoDB는 항상 교착 상태를 즉시 감지하나요?
InnoDB의 교착 상태 감지기는 잠금 충돌이 발생할 때 동기적으로 실행됩니다. 감지는 거의 즉각적입니다.innodb_deadlock_detect = OFF로 비활성화하고 innodb_lock_wait_timeout에만 의존할 수 있으며, 일부 고동시성 워크로드가 성능을 위해 그렇게 하지만 드문 일입니다.
모든 교착 상태를 예방할 수 있나요?
아니요. 교착 상태는 잠금을 사용하는 동시 트랜잭션의 근본적인 속성입니다. 일관된 잠금 순서와 짧은 트랜잭션으로 빈도를 줄일 수 있으며, 항상 교착 상태에 대한 재시도 로직을 작성해야 합니다.문제 해결
함께 보기
- MySQL의 트랜잭션 — 교착 상태를 가능하게 하는 트랜잭션 모델
- InnoDB의 데이터 저장 방식 — InnoDB 행 수준 잠금이 내부에서 동작하는 방식