앞선 글 마지막에서 트랜잭션 관리 시 주의사항과 함께
"두 개 이상의 트랜잭션이 서로 상대방의 락 해제를 기다리며
무한정 대기에 빠지는 현상"인 데드락(Deadlock)을 간략히 소개했었다.
실제로 데이터베이스를 운영하다 보면
단순히 이론으로만 접했던 데드락이
실제 장애의 원인으로 자주 등장한다는 사실을 경험하게 됩니다.
특히 여러 사용자가 동시에 데이터를 수정하거나,
여러 트랜잭션이 복잡하게 엮이는 실시간 환경에서는
데드락이 발생할 가능성이 더욱 높아집니다.
이번 글에서는
데드락이란 정확히 무엇이며,
왜 발생하고, 어떤 식으로 실무에서 문제를 일으키는지
실제 SQL 실습 예시통해 직접 실험 해봤다.
앞선 이론적 설명에서 한걸음 더 나아가
"실제로 마주치는 데드락 장애를 어떻게 다룰 것인가?"에 집중합니다.
아래 쿼리는 MySQL 등에서 두 개의 세션을 이용해 직접 데드락 상황을 유도하는 기본 예시입니다.
참고로 데드락 상황은 2개의 서로 다른 "세션"(=연결창, 터미널, 쿼리 실행창)에서 발생시켜야 합니다.
1. 실습용 테이블 생성 및 데이터 삽입
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가 자동으로 둘 중 하나를 롤백시켜 데드락을 해소합니다.
트랜잭션의 락 점유 순서를 동일하게 맞추면 데드락이 발생하지 않습니다.
1. 첫 번째 세션
2. 두 번째 세션 (첫 번째 세션이 끝난 후 시작)
핵심: 여러 트랜잭션이 동일한 자원에 접근할 때
“항상 같은 순서로 락을 걸면” 데드락이 발생하지 않습니다.
이 기법들은 DBMS 내부에서 자동 구현이 되어 있다.
트랜잭션을 오래 열어두면 락이 계속 유지되어 데드락 확률이 올라감.
각 작업(UPDATE, INSERT, DELETE)마다 바로 커밋하는 습관이 중요하다.
START TRANSACTION
UPDATE accounts SET balance = balance - 100 WHERE id = 1
COMMIT```
모든 트랜잭션이 동일한 테이블/행에 항상 같은 순서(예: 오름차순)로 접근해야 한다.
순서가 꼬이면 "서로 락을 잡고 대기"하는 데드락이 발생할 수 있다.
SELECT * FROM accounts WHERE id IN (1, 2) ORDER BY id FOR UPDATE```
여러 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) 와 같은 옵션으로
트랜잭션 대기 시간을 제한하고, 자동 롤백을 설정할 수도 있습니다.
[MySQL] Deadlock의 원인과 예방 방법
Preventing deadlock with timestamps: wound-wait scheme
좋은 글 감사합니다