FK 제약조건과 UPDATE 간의 데드락

Lee Jin Hyuk·2025년 8월 31일

주차권 프로젝트

목록 보기
1/2

주차권 선착순 시스템에서 발생한 데드락 해결기 🚗

들어가며

주차권(선착순) 시스템을 개발하던 중 동시 신청 상황에서 데드락이 발생하는 문제를 만났다. 선착순 시스템 특성상 데이터 유실은 치명적이기 때문에, 이 문제를 해결하는 과정을 기록해보려 한다.

🏗️ 시스템 구조

데드락이 발생한 관련 테이블은 다음과 같다:

  • registration 테이블: 주차권 신청 정보
  • sector 테이블: 주차 구역 정보 (잔여 자리 수 등)
  • FK 관계: registration.sector_id → sector.id

기본적으로 신청자가 신청을 완료하면 Sector(주차 구역 정보)의 잔여 여석을 -1 하는 플로우다.

🚨 문제 발생 상황

신청자 A와 신청자 B가 동시에 신청 요청을 보냈을 때 데드락이 발생했다.

대기열 큐를 도입한 서비스 특성상 롤백된 데이터가 유실될 수 있는 상황이 발생했는데...

물론 Redis Sorted Set과 peek-pop 구조로 되어있어 데이터 유실 방지 아키텍처로 구성되어있어 실제 유실되진 않는다.
데이터 유실 방지 아키텍처 관련해선 시간이 되면 나중에 포스팅 하려 한다.

🔍 문제 발생 원인 분석

데드락 발생 시나리오

1️⃣ T1 (트랜잭션 1): INSERT 문 실행 → sector 테이블에 공유락(Shared Lock) 획득
2️⃣ T2 (트랜잭션 2): INSERT 문 실행 → sector 테이블에 공유락(Shared Lock) 획득
3️⃣ T1: UPDATE 문 실행 시도 → sector 테이블에 배타락(Exclusive Lock) 대기
4️⃣ T2: UPDATE 문 실행 시도 → sector 테이블에 배타락(Exclusive Lock) 대기

문제가 되는 코드

-- T1, T2가 동시에 실행하는 로직
START TRANSACTION;

-- 1. INSERT 시 FK 제약조건 검사를 위해 sector 테이블에 공유락 획득
INSERT INTO registration (user_id, sector_id, ...) 
VALUES (?, ?, ...);

-- 2. sector 테이블의 잔여 자리 수를 감소시키기 위한 UPDATE
-- 이때 배타락이 필요하지만 이미 다른 트랜잭션이 공유락을 보유중
UPDATE sector 
SET remaining_spots = remaining_spots - 1 
WHERE id = ?;

COMMIT;

왜 데드락이 발생했을까?

핵심 원인 3가지:

  1. FK 제약조건 검사 시 참조되는 테이블(sector)에 공유락이 걸림
  2. 같은 트랜잭션에서 해당 테이블을 UPDATE하려면 배타락이 필요
  3. 두 개 이상의 트랜잭션이 동시에 같은 레코드에 공유락을 보유하면, 누구도 배타락을 획득할 수 없는 데드락 상황 발생

✅ 해결 방안: SELECT FOR UPDATE

해결 코드

START TRANSACTION;

-- 🔑 먼저 배타락을 획득하여 다른 트랜잭션의 접근 차단
SELECT * FROM sector WHERE id = ? FOR UPDATE;

-- 이후 INSERT 실행 (이미 배타락이 있으므로 추가 락 불필요)
INSERT INTO registration (user_id, sector_id, ...) 
VALUES (?, ?, ...);

-- UPDATE 실행 (이미 배타락 보유 상태)
UPDATE sector 
SET remaining_spots = remaining_spots - 1 
WHERE id = ?;

COMMIT;

해결 원리

먼저 배타락을 획득함으로써 INSERT, UPDATE 시 추가적인 락 획득 시도를 하지 않도록 한다.

✅ T1: SELECT FOR UPDATE (sector에 배타락) → INSERT → UPDATE
⏳ T2: SELECT FOR UPDATE 시도 → T1의 배타락 때문에 대기 → T1 완료 후 진행

결과: 한 번에 하나씩만 처리되어 충돌 없음

🤔 Trade-off 고려사항

단점

  • 동시성이 떨어짐 (순차 처리)
  • 처리 속도가 상대적으로 느려질 수 있음

장점

  • 데이터 일관성 보장
  • 데드락 완전 방지
  • 안정적인 서비스 운영

결론

짧은 시간 안에 트래픽이 몰리는 시스템 특성상 비동기 작업이 중요하긴 하지만, 소중한 데이터의 일관성을 지키는 것이 더 중요하다고 생각한다.

특히 선착순 시스템에서는 공정성이 가장 중요한 요소이기 때문에, 약간의 성능을 포기하더라도 안정성을 택하는 것이 옳은 선택이라고 생각한다.

내가 먼저 신청했는데 내가 떨어지면 눈물 날 듯 ㅜㅜㅜ..

마무리

데드락 문제는 동시성이 높은 시스템에서 언제든 발생할 수 있는 문제다. 특히 FK 제약조건이 있는 테이블에서 INSERT와 UPDATE가 동시에 일어나는 경우에는 더욱 주의해야 한다.

이번 경험을 통해 락의 종류획득 순서가 얼마나 중요한지 다시 한번 깨달았다. 앞으로도 이런 동시성 이슈들을 미리 예측하고 대응할 수 있는 개발자가 되고 싶다.

0개의 댓글