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