Redis -2

2025. 6. 10. 17:48·Backend/DB
728x90

여러 사용자가 동시에 같은 데이터를 수정하려고 할 때 경쟁 조건이 발생하는 경우 Redis는 어떻게 대처할까?

Redis는 경쟁 조건을 방지하기 위해 두 가지 접근 방식인  Optimistic Lock, Pessimistic Lock 을 사용한다.

시나리오 전략 이유
재고 감소, 좋아요 수 증가 Optimistic 충돌 적고 성능 중요
결제, 티켓팅, 중복 요청 방지 Pessimistic 무조건 1개만 처리해야 함

Optimistic Lock (낙관적 락)

문제가 발생하지 않을 것이라는 낙관적인 개념으로
동시 접근을 허용하되, 마지막에 충돌이 발생하면 롤백하는 방식이다.

Redis에서는 `WATCH`, `MULTI`, `EXEC` 명령어를 사용하여 구현한다.

특징

항목 설명
장점 락을 사용하지 않아 빠르고 병렬 처리에 유리
단점 충돌이 많으면 retry로 인해 성능 저하
적합한 경우 업데이트 충돌 가능성이 낮은 경우

구현 방식

WATCH mykey           # mykey를 감시 시작
MULTI                 # 트랜잭션 시작
SET mykey "new-value" # 여러 명령어를 큐에 담음
EXEC                  # 트랜잭션 실행 (다른 클라이언트가 mykey를 바꿨다면 실패)

WATCH 이후 다른 클라이언트가 mykey를 변경하면 EXEC 실행 시 트랜잭션은 실패한다. (롤백)
👉 실패 시 다시 시도(retry) 한다


Pessimistic Lock (비관적 락)

문제가 발생할 것이라는 비관적 개념으로
공유자원에 접근하는 동안 다른 사용자의 접근을 막는다.

Redis에서는 `SENTX` 나 `Redlock` 알고리즘으로 구현한다.

특징

항목 설명
장점 경쟁 없이 안전하게 자원을 보호
단점 락 획득/해제 비용, 데드락 위험
적합한 경우 공유 자원에 대한 충돌 가능성이 높고 꼭 순서를 보장해야 하는 경우

구현 방식 (SENTX)

SETNX lock_key "locked"   # 락 획득 시 1 반환 (성공)
EXPIRE lock_key 10        # 타임아웃 설정 (10초)
...                       # 자원 사용
DEL lock_key              # 락 해제

Redlock 알고리즘 (비관적 락)

단일 Redis 서버에서 SENTX로 락을 구현 시 Redis 서버가 다운되면 락 자체도 유실되어 신뢰성이 떨어진다.

∴ 분산 환경에서는 여러 Redis 노드에 락을 동시에 걸고, 일부가 실패해도 안전하게 락을 유지해야 한다.

📌 작동 원리
N개의 서로 다른 Redis 인스턴스 중에서 과반수 이상 (M)에게 락을 성공적으로 설정하면 락 획득 성공으로 간주
`M = N/2+1` (보통 N=5, M=3)
특성 설명
Safety (안전성) 오직 하나의 클라이언트만 락을 획득할 수 있음
Liveness (활성성) 일부 Redis 노드가 실패하더라도 락을 획득 가능
Fault-tolerance (내결함성) 다수 노드가 살아있으면 시스템은 정상 동작

락 획득 과정

  1. 현재 시간 기록 (밀리초 기준)
  2. 모든 Redis 인스턴스에 동일 key, 동일 value로 락 시도
    • 예: SET lock_key value NX PX 10000
      (NX: 해당 키가 존재하지 않을 때만 설정, PX: TTL 10초)
  3. 응답을 받은 Redis 인스턴스가 M 이상인지 check
  4. 총 소요 시간이 TTL 보다 작을 경우 락 획득 성공
  5. 실패하면 전체 락 해제 후 재시도 (retry with backoff)

락 해제 과정

락을 해제할 때는 반드시 락을 설정한 사용자만 해제할 수 있어야 한다.

락 설정 시 사용하는 랜덤한 고유 값 (UUID 등)을 저장해 두었다가, 락 해제 시 key의 value값이 동일한 경우만 삭제한다.

-- Lua 스크립트 예시 (atomic하게 수행)
if redis.call("get", KEYS[1]) == ARGV[1] then
  return redis.call("del", KEYS[1])
else
  return 0
end

결론

Redlock은 단일 Redis 인스턴스 기반 락보다 훨씬 안전한 락 시스템이며, 특히 다음과 같은 경우에 적합하다.

  • 분산 시스템 환경 (여러 서버에서 동시에 접근)
  • 락이 반드시 정확하게 지켜져야 하는 경우 (예: 주문 중복 방지, 결제 동시 처리)
항목  안전하게 락 처리하려면
TTL 무조건 설정 (자동 해제용)
UUID 락 소유자 식별을 위해 사용
비정상 종료 TTL로 방지, UUID로 정확히 해제
장기 작업 필요 시 락 연장 로직 구현

❓ 응답을 받은 Redis 인스턴스가 M 미만 

락을 시도했는데, 성공한 Redis 인스턴스 수가 과반수보다 작은 상황

분산 시스템에서는 일부 노드가 네트워크 단절되거나 느려질 수 있기 때문에
과반수 이상이 동의하지 않으면 `안전하지 않은 락`일 수 있다고 간주한다.

만일 과반수를 넘지 못하는 락을 신뢰한다면:

  • 다른 클라이언트도 락을 잡았다고 착각 가능
  • 동시에 같은 자원에 접근하여 Race Condition 발생 가능

❓ 락을 얻은 총 소요시간 ≥ TTL 

락이 획득되긴 했지만, 그 시점엔 이미 TTL이 거의 끝났거나 지나서 자원 보호가 되지 않는 상황
👉 안전하지 않은 락이므로 실패로 간주한다.

예시

  • TTL: 10,000ms (10초)
  • Redis  5개 중 3개에서 락을 성공적으로 설정
  • 3개에서 락을 모두 얻는 데 걸린 시간: 11,000ms (> TTL)

락을 설정한 시점은 서로 다르고 각 Redis 인스턴스는 락을 잡은 후부터 TTL만큼만 유지
즉, 락을 설정한 순간부터 각 노드는 10초 후에 자동으로 락을 해제한다.

노드 락 설정 시간 TTL 만료 시간
Redis A 0ms 10,000ms
Redis B 5,000ms 15,000ms
Redis C 11,000ms 21,000ms

→ 락을 11초에 얻었지만, Redis A에서는 이미 TTL이 만료됨
→ 다른 클라이언트는 Redis A에서 락이 해제된 것으로 보고 다시 락을 잡을 수 있음

🟥 그러면 두 클라이언트가 동시에 자원에 접근하는 Race Condition이 발생할 수 있음

📌 락을 잡는 데 걸린 시간이 TTL 보다 길면, 락을 실패로 간주하고 즉시 해제 수 재시도해야 한다.

 

DeadLock

클라이언트가 락을 잡고 작업 중 죽거나 네트워크 단절되는 경우, 락을 해제하지 못하게 되는데
이런 상태에서 락이 계속 유지되면 다른 클라이언트가 접근하지 못한다. 👉 데드락 발생

Redis 분산 락에서 Deadlock 예방/회피 방법

방법 설명
TTL 설정 락 자동 만료로 데드락 방지
UUID 사용 락 소유자 확인으로 안전한 해제
재시도 + 백오프 랜덤 시간 대기 후 재시도 → 과도한 경합으로 인한 데드락 가능성 감소
락 순서 고정 교착 상태 순환 대기 방지
락 연장 (신중) 긴 작업 시 락 유효 기간 연장
최소 락 시간 락 획득 기간 최소화

 

 

728x90
'Backend/DB' 카테고리의 다른 글
  • 트랜잭션 분산 환경 처리
  • DB 분산 구조
  • Redis - 1
  • 트랜잭션 (Transaction)
0woy
0woy
Algorithm, CS, Web 등 배운 내용을 기록합니다.
  • 0woy
    0woy dev
    0woy
  • 전체
    오늘
    어제
  • 🌐 LANGUAGE
    • 분류 전체보기 (80)
      • Backend (21)
        • JAVA (7)
        • DB (11)
        • Spring (1)
        • Spring Security (2)
      • Computer Science (22)
        • 네트워크 (9)
        • 운영체제 (5)
        • 보안 (7)
      • Frontend (15)
        • HTML5 (1)
        • CSS (1)
        • JS (4)
        • Vue 3 (9)
      • PS (16)
        • LeetCode (2)
        • Baekjoon (1)
        • Programmers (1)
        • 알고리즘 (12)
      • Dev Trivia (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    속성
    security
    dfs
    대칭키
    BFS
    비밀키
    공개키
    set
    JDBC
    Spring
    JS
    PreparedStatement
    select
    Graph
    Props
    CA
    function
    https
    leetcode
    DP
    shortestpath
    그래프
    javascript
    가용성
    RDB
    Filter
    tcp
    Vue3
    트리
    java
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
0woy
Redis -2
상단으로

티스토리툴바