배경 및 목표

공채 시즌에는 수백 명이 동시에 지원합니다. 수험번호는 공고 단위로 유니크해야 하는데, “현재 최대값 조회 → +1 → 저장” 과정의 시간 간격에 다른 요청이 끼어들면 같은 번호가 배정됩니다.

sequenceDiagram
    participant A as 지원자 A
    participant B as 지원자 B
    participant DB as Database
    A->>DB: 최대 수험번호 조회 (100)
    B->>DB: 최대 수험번호 조회 (100)
    A->>A: 다음 번호 = 101
    B->>B: 다음 번호 = 101
    A->>DB: 101 배정 ✅
    B->>DB: 101 배정 ❌ 중복!

목표

  • 공채 시즌 동시 지원에서도 공고 단위 수험번호를 100% 유니크하게 배정한다.
  • DB 커넥션 장기 점유·데드락 없이 낮은 지연으로 처리한다.

해결 방법과 해결 후보군

1. DB 락 대신 Redis를 선택한 이유

방식문제점
비관적 락 (SELECT FOR UPDATE)DB 커넥션 오래 점유, 데드락 위험
낙관적 락 (Version)공채 시즌 동시 요청 많으면 재시도 빈번
Redis 분산락 + INCRms 단위 락, 원자적 증가, TTL 데드락 방지

Redis는 인메모리 기반으로 ms 단위 락 획득/해제가 가능하고, INCR이 원자적이므로 별도 재시도 없이 유니크를 보장합니다.

2. SET NX + INCR 원자적 연산

SET lock:target:123 "holder" NX EX 10  # 분산락 획득 (TTL 10초)
INCR sequence:target:123               # 원자적 증가

“조회 → 계산 → 저장” 3단계가 INCR 1단계로 줄어들어 경합 자체가 사라집니다. TTL을 설정하여 네트워크 장애 시에도 락이 자동 해제되어 데드락을 방지합니다.

sequenceDiagram
    participant E as 지원 엔드포인트
    participant P as 수험번호 처리기
    participant R as Redis
    participant DB as Database
    E->>P: 지원 내부 이벤트
    P->>R: SET key value NX (락 획득)
    alt 락 성공
        R-->>P: OK
        P->>R: INCR (원자적 증가)
        R-->>P: 101
        P->>DB: 수험번호 101 배정
        P->>R: 락 해제
    else 락 실패
        R-->>P: NULL
        P->>P: 대기 후 재시도
    end

결과

지표결과
유니크 보장100% (100건/초)
락 획득/해제1~2ms
DB 락 대비10배 빠름
데드락 위험TTL로 방지

모니터링

  • 수험번호 중복 발생 건수(0 유지)를 관측한다.
  • Redis 락 획득/해제 지연(p99)과 락 실패 재시도 횟수를 관측한다.