[친구하자] 매칭 대기열 만들면서 정리한 Redis 명령어 치트시트 (SET/ZSET편)

친구하자 프로젝트 개발 중 랜덤 매칭 대기열을 만들면서 정리한 Redis 명령어 모음집입니다.

카테고리별 2인 매칭을 구현하며 실제로 쓴(또는 고민했던) 명령어들을 짧게 정리. 예시 키는 {cat:123} 해시태그로 Redis Cluster 슬롯을 고정하는 패턴을 사용.


ZSET (Sorted Set) — 순번/대기시간/우선순위가 필요할 때

  • ZADD key score member [score member ...] 멤버 추가/갱신. score가 작을수록 앞쪽.

    ZADD wait:z:{cat:123} 1725430000123 101
    
  • ZRANGE key start stop [WITHSCORES | REV | BYSCORE ...] 정렬 순서대로 조회. 삭제는 아님!

    ZRANGE wait:z:{cat:123} 0 1              # 가장 오래 기다린 2명
    ZRANGE wait:z:{cat:123} 0 9 WITHSCORES    # 상위 10명 + score
    
  • ZRANK key member 멤버의 순위(0-base).

    ZRANK wait:z:{cat:123} 101               # 0이면 1번째
    
  • ZSCORE key member 멤버의 score(대기 시작 시각 등).

    ZSCORE wait:z:{cat:123} 101
    
  • ZCARD key 멤버 수.

    ZCARD wait:z:{cat:123}
    
  • ZREM key member [member ...] 멤버 삭제.

    ZREM wait:z:{cat:123} 101 205
    
  • ZRANDMEMBER key [count] [WITHSCORES] (6.2+) 무작위 조회(삭제 아님).

    ZRANDMEMBER wait:z:{cat:123} 2
    
  • ZPOPMIN key [count] / ZPOPMAX 꺼내며 삭제(원자). 대기순 매칭에 유용.

    ZPOPMIN wait:z:{cat:123} 2
    

SET (집합) — 중복 없는 랜덤 풀

  • SADD key member [member ...] / SREM key member [...] 추가/삭제 (중복 불가).

    SADD matching:queue:{cat:123} 101
    SREM matching:queue:{cat:123} 101
    
  • SPOP key [count] 무작위 추출+삭제(원자). 랜덤 매칭에 간단·빠름.

    SPOP matching:queue:{cat:123} 2
    
  • SCARD key 멤버 수.

    SCARD matching:queue:{cat:123}
    

TTL/키 유틸 (대기 상태 자동 청소용)

  • SET key value EX <sec> NX 선점 + TTL(중복 입장 방지).

    SET user:index:42 "cat=123|queue=q_..." EX 600 NX
    
  • EXPIRE key <sec> / TTL key 키 만료/조회. 주의: TTL은 키 전체에 붙음(멤버 단위 X).

베스트 프랙티스: 대기열 컬렉션(SET/ZSET)엔 TTL 금지, 개별 사용자 상태 키에만 TTL.


매칭에서 자주 쓰는 레시피

1) 대기순(공정성) 매칭

  • 방법 A(2단계): ZRANGE로 상위 N 조회 → ZREM로 제거

    ZRANGE wait:z:{cat:123} 0 1         # 후보 조회
    ZREM   wait:z:{cat:123} 101 205     # 확정 후 제거
    
  • 방법 B(원자): ZPOPMIN wait:z:{cat:123} 2 (조회+삭제가 한 번에)

2) 랜덤 매칭

  • ZSET만 쓸 때(원자화는 Lua 추천):

    • ZRANDMEMBER로 조회 → 같은 로직 안에서 ZREM
  • SET을 함께 쓸 때:

    • SPOP matching:queue:{cat:123} 2 (원자 랜덤 추출)

3) 하이브리드(랜덤 80% + 대기순 20%)

  • 하나의 Lua/Functions에서 분기:

    • 랜덤: ZRANDMEMBERZREM
    • 대기순: ZRANGEZREM (또는 ZPOPMIN)

4) 원자성 보장(동시성 안전)

  • 조회(선정)와 삭제를 한 덩어리로: Lua/Functions로 감싸기

    • “이미 다른 워커가 먼저 가져간” 케이스를 **ZREM 반환값(0/1)**로 판단해 필터링.

흔한 실수 & 주의

  • ZRANGE/ZRANDMEMBER 호출하고 ZREM을 빼먹음 → 큐에 그대로 남아 중복 매칭 발생.
  • 컬렉션 키에 TTL을 걸어 전체 대기열이 사라짐. TTL은 개별 사용자 키(예: user:queued:{cat:123}:42)에만.
  • Redis Cluster 사용 시, 스크립트 KEYS는 같은 해시태그({cat:123})로 묶기. 전역 키는 Lua에서 다루지 말고 자바에서만.

시간 복잡도

  • ZADD/ZREM/ZRANKO(log N)
  • ZRANGEO(k) (반환 수)
  • ZPOPMIN/MAXO(k log N)
  • SADD/SREM/SPOP평균 O(1)

미니 예시: 대기열 입장 & 매칭

# 입장
ZADD wait:z:{cat:123} 1725430000456 101          # 대기 시작 시각(밀리초)
SET  user:queued:{cat:123}:101 q_101_123 EX 600   # 개별 TTL

# 대기순 매칭 2명 (원자)
ZPOPMIN wait:z:{cat:123} 2                        # -> [101,score],[205,score]

# 랜덤 매칭 2명 (Lua로 원자화 권장)
-- 조회
ZRANDMEMBER wait:z:{cat:123} 2
-- 같은 로직 안에서 제거
ZREM wait:z:{cat:123} 101 205

한 줄 정리

  • ZSET = 순번/대기시간/공정성, SET = 단순 랜덤 풀
  • ZRANGE/ZRANDMEMBER(조회) 뒤엔 반드시 ZREM(삭제)
  • 경쟁 환경에선 Lua로 “조회→삭제”를 원자화
  • TTL은 개별 상태 키에만, 대기열 컬렉션엔 금지