어떤 개념일까?
데드락이 감지돼도 데이터베이스는 멈추지 않는다. 싸이클에 얽힌 트랜잭션 몇 개만 교착 상태일 뿐이고, DB는 그 중 하나를 골라 강제 롤백(victim)해서 매듭을 끊는다.
싸이클 밖의 트랜잭션들은 처음부터 끝까지 정상적으로 돌아간다.
데드락 처리는 감지 후 희생자 롤백 방식이 표준이라고 한다.
데드락은 둘 이상의 트랜잭션이 서로 쥐고 있는 락을 기다리며 순환 대기 에 빠진 상태이다.
DB는 이 사이클을 찾아내서 한쪽을 끊어주는 역할을 한다.
어떤 문제를 해결하려고 나왔을까? 왜 사용 할까?
DB는 동시성과 일관성을 위해서 락을 사용하는데, 트랜잭션마다 락을 잡는 순서가 엇갈리면 순환 대기가 구조적으로 발생할 수 밖에 없습니다.
이론적으로는 데드락을 완전히 예방하려면 트랜잭션이 필요한 모든 락을 미리 알고 한 번에 잡거나 전역 락 순서를 강제해야 하는데, 비현실적이다.
그래서 대부분 DB는 일단 데드락이 생기게 된다면, 한쪽을 롤백해서 복구한다는 유연한 전략을 택한다고 한다.
어떻게 동작하나?
1. 대기 그래프 (wait-for Graph)
대기 그래프 (wait-for Graph)를 통해서 트랜잭션, 간선 별로 T1 → T2 형태도 각 트랜잭션마다 어느 트랜잭션에서 가지고 있는 락을 기다린다는 의미를 부여한다.
해당 그래프를 검사하는 트리거 방식은 DB 마다 다르다고 한다.
능동 감지 (MySQL InnoDB)
락 요청이 대기에 들어갈 때마다 대기 그래프에서 사이클을 검사한다.
innodb_deadlock_detect가 기본이다.
타임아웃 트리거 감지 (PostgreSQL)
평소에 검사를 돌지 않다가, 락 대기가 deadlock_timeout(기본 1초)을 넘기면
그제서야 그래프를 만들어 검사한다.
대부분의 락 대기가 짧으면 감지 비용을 거의 안낸다.
순수 타임아웃
innodb_lock_wait_timeout(기본 50초) 같은 값을 넘기면 그냥 그 트랜잭션을 죽인다.
그래프 감지가 놓친 경우의 안전망 역할을 한다.
2. 희생자 선정 후 강제 롤백
데드락 사이클이 판단되면 DB는 한 트랜잭션을 골라 롤백해 매듭을 끊는다. InnoDB는 보통 수정한 행이 가장 적은 트랜잭션을 희생자로 삼는다.
MySQL: 1213 Deadlock found
PostgreSQL: ERROR: deadlock detected
3. 재시도는 애플리케이션에서
희생당한 트랜잭션이 롤백되어 처음부터 다시 시도해야 한다. 즉, 데드락은 운영 중 정상적으로 발생할 수 있는 이벤트이고, 앱은 이 에러를 잡아 재시도하게 한다.