Phase 3레슨 14

Distributed Lock

Redis SETNX, Redlock, Zookeeper, Fencing Token — 분산 환경에서 상호 배제

💡ELI5·

화장실 열쇠와 분산 락

Client1 ──SETNX lock──→ Redis (락 획득)
Client2 ──SETNX lock──→ Redis (거부, 이미 잠김)
Client1 ── 작업 수행 ──
Client1 ──DEL lock──→ Redis (락 해제)
Client2 ──SETNX lock──→ Redis (락 획득 가능)
💡 비유분산 락 = 화장실 열쇠. 한 사람이 열쇠를 가지고 있으면 다른 사람은 대기. 문제: 열쇠를 가진 사람이 쓰러지면? (TTL로 자동 해제). 열쇠를 잃어버리면? (Redlock 다수결).
🔬Deep Dive·

Redis 기반 분산 락

# 기본 락 (TTL 필수 — 장애 시 자동 해제)
SET lock:order:123 "client-uuid" NX EX 30

# 락 해제 (Lua 스크립트로 원자적 검증 + 삭제)
# 자신의 UUID 확인 후 삭제 (다른 사람 락을 삭제하지 않도록)
EVAL "
  if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
  else
    return 0
  end
" 1 lock:order:123 "client-uuid"
주의: 락을 잡은 후 작업이 TTL보다 오래 걸리면 락이 풀려 다른 클라이언트가 진입. 락 연장(Renewal/Watchdog)이 필요할 수 있다.
🔬Deep Dive·

Redlock — 다수결 락

5개의 독립 Redis 노드:
  Client → 동시에 5개 노드에 SET NX 요청
  과반수(3개 이상) 성공 + 유효시간 남음 → 락 획득
  과반수 실패 → 락 획득 실패, 성공한 노드에서 락 해제

Redlock은 단일 Redis 장애를 방지하기 위해 여러 노드의 다수결을 사용. 하지만 Martin Kleppmann은 GC pause와 시계 드리프트로 인해 안전성이 깨질 수 있다고 지적했다.

🔬Deep Dive·

Fencing Token — Kleppmann의 해법

Client A: 락 획득 (token=1) → GC pause (길어짐)
          → 락 TTL 만료
Client B: 락 획득 (token=2) → DB에 token=2 기록, 작업 수행
Client A: GC 복귀 → DB에 쓰기 시도 (token=1)
          → DB: "token=1 < token=2, 거부" ← Fencing!

Fencing Token은 락 획득 시 단조 증가하는 토큰을 부여. 공유 자원이 토큰을 기록하여, 낮은 토큰의 요청을 거부. GC pause로 인한 stale 락 소유자의 쓰기를 방지한다. 단, 공유 자원(DB 등)이 토큰 검증을 지원해야 한다.

⚖️Trade-off·

분산 락 구현 방식 비교

방식안전성성능복잡성적합
Redis SETNX낮음 (단일 장애)매우 높음낮음캐시, 짧은 임계 구역
Redlock중간 (Kleppmann 비판)높음중간높은 성능 필요, 일시적 불일치 허용
Zookeeper높음 (세션 기반)낮음높음강한 안전성 필요
etcd높음 (Raft 기반)중간중간Kubernetes, 설정 동기화
정말 분산 락이 필요한지 먼저 확인하라. DB 행 락, 유니크 제약조건, Idempotency Key 등으로 해결할 수 있는 경우가 많다.

❓ 체크포인트 질문

  1. 1.Redis SETNX로 분산 락을 만들 때 발생할 수 있는 문제는?
  2. 2.Redlock이 Kleppmann의 비판을 받는 핵심 이유는?
  3. 3.Fencing Token이 분산 락의 안전성을 어떻게 보장하는가?
  4. 4.Zookeeper 기반 락이 Redis 기반 락보다 안전한 이유는?
  5. 5.분산 락이 실제로 필요한 상황과 불필요한 상황의 예는?