여러 서버(프로세스)가 하나의 자원을 동시에 건드리지 못하게 막는 락. 단일 프로세스의 뮤텍스로는 해결되지 않는다 — 서버가 여러 대면 각자의 메모리에 각자의 락이 있을 뿐이기 때문이다.
Redis로 만드는 최소 형태
SET lock:order:123 <랜덤토큰> NX PX 3000
| 옵션 | 의미 | 없으면 |
|---|---|---|
NX | 키가 없을 때만 성공 | 남의 락을 덮어씀 |
PX 3000 | 3초 후 자동 만료 | 락을 쥔 채 죽으면 영원히 잠김 |
<랜덤토큰> | 내가 건 락인지 식별 | 남의 락을 푸는 사고 |
해제는 반드시 원자적으로
-- 토큰이 내 것일 때만 삭제 (Lua 스크립트로 원자 실행)
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
왜 확인이 필요한가
- A가 락 획득 (만료 3초)
- A의 작업이 4초 걸림 → 3초에 락 자동 만료
- B가 락 획득
- A가 작업을 마치고 DEL → B의 락을 지워 버림
- 5. C도 락 획득 → B와 C가 동시에 임계 구역 — ← 락이 무너짐
토큰을 확인하면 4번에서 A는 "내 토큰이 아니네" 하고 지우지 않는다.
그래도 남는 문제 — 2번 자체
토큰 확인은 남의 락을 푸는 것만 막는다. A가 만료된 줄 모르고 계속 작업하는 상황은 그대로다. 그래서
- 작업 시간을 만료 시간보다 확실히 짧게 잡거나
- watchdog으로 작업 중 주기적으로 만료를 연장하거나(Redisson이 이렇게 한다)
- 애초에 락에 정합성을 걸지 않는다
마지막 항목이 가장 중요하다
Redis 단일 노드가 죽으면 락 정보가 사라진다. Redlock(여러 노드 과반 획득)이 제안됐지만 안전성 논쟁이 있다(마틴 클레프만의 비판).
그래서 실무의 정답은 이렇다.
락은 성능 최적화(중복 작업 방지)로 쓰고, 최종 정합성은 DB가 보장하게 한다.
-- 락이 뚫려도 이건 뚫리지 않는다
UPDATE stock SET qty = qty - 1 WHERE id = 1 AND qty >= 1;
-- 또는 유니크 제약으로 중복 삽입 차단
락이 실패해도 데이터가 깨지지 않는 설계를 먼저 만들고, 락은 그 위에 얹는다.
면접 답변 골격
"Redis 분산 락은 NX·PX·토큰 검증이 기본이고, 해제는 Lua로 원자화합니다. 다만 락만으로 정합성을 보장하지 않고 DB의 조건부 갱신이나 유니크 제약을 최종 방어선으로 둡니다."