개발/REDIS

#11 🗂️ 클러스터: 샤딩·해시 슬롯·제약

cedis 2026. 3. 19. 11:46
Redis 서버 한 대의 메모리가 부족해졌을 때, 여러 노드에 데이터를 나눠 저장하는 클러스터를 이해합니다.

💾 "메모리가 부족해지면 어떻게 하나요?"

서비스가 성장하면서 Redis에 저장해야 할 데이터가 늘어납니다. 처음에는 서버 메모리를 늘리면(Scale-Up) 됩니다. 하지만 한 대 서버의 메모리에는 한계가 있습니다. 그때는 서버를 여러 대로 늘리는(Scale-Out) 방법이 필요합니다.

Redis Cluster는 데이터를 여러 노드에 나눠서 저장하는 공식 분산 솔루션입니다. 각 노드는 전체 데이터의 일부만 담당하고, 클라이언트는 어떤 노드에 요청해도 올바른 노드로 자동 리다이렉트됩니다.

Cluster는 Sentinel과 다릅니다. Sentinel은 "한 대가 죽으면 다른 한 대가 대신"하는 고가용성(HA)이고, Cluster는 "데이터를 여러 대에 나눠 저장"하는 수평 확장(Scale-Out)입니다. 물론 Cluster도 각 샤드마다 Replica를 두어 HA를 함께 구성합니다.

🎰 핵심 개념 1: 16,384 해시 슬롯

Redis Cluster는 전체 키 공간을 16,384개의 슬롯으로 나눕니다. 모든 키는 CRC16 알고리즘으로 해시값을 계산한 뒤 16,384로 나눈 나머지로 슬롯 번호가 결정됩니다.

💡 슬롯 계산 공식
slot = CRC16(key) % 16384

예: "user:1000" → CRC16 계산 → 12345 % 16384 = 12345번 슬롯
3개 Primary 노드라면 슬롯이 균등 분배됩니다:
노드A: 0~5460, 노드B: 5461~10922, 노드C: 10923~16383

클라이언트가 어떤 노드에 요청을 보내든, 그 노드가 해당 키의 슬롯을 담당하지 않으면 MOVED 리다이렉트 응답을 보냅니다. 클라이언트는 이를 받아 올바른 노드로 재요청합니다. redis-cli -c 옵션을 쓰면 이 리다이렉트가 자동으로 처리됩니다.

🏷️ 핵심 개념 2: 해시 태그 — 같은 슬롯 강제

Cluster에서는 여러 키를 한 번에 처리하는 멀티키 명령(MGET, MSET, SUNIONSTORE 등)이 제한됩니다. 관련된 키들이 서로 다른 노드에 있을 수 있기 때문이죠.

이를 해결하는 것이 해시 태그(Hash Tag)입니다. 키 이름에서 {} 안의 내용만으로 슬롯을 계산합니다. 같은 태그를 쓰면 관련 키들이 같은 슬롯(같은 노드)에 저장됩니다.

redis-cli
# 해시 태그 없이: 다른 슬롯에 저장될 수 있음
SET order:1000:item "coffee"    # 슬롯 A
SET order:1000:status "pending" # 슬롯 B (다른 노드!)

# MGET 오류 발생 가능
MGET order:1000:item order:1000:status
# → CROSSSLOT Keys in request don't hash to the same slot

# 해시 태그 사용: 같은 슬롯 강제
SET order:{1000}:item "coffee"    # CRC16("1000") % 16384
SET order:{1000}:status "pending" # 동일 슬롯!

# 이제 MGET 가능
MGET order:{1000}:item order:{1000}:status
# → 1) "coffee"  2) "pending"

🔬 Lab 10: 6노드 클러스터 생성

Redis Cluster 최소 구성은 3개의 Primary + 각각 1개의 Replica = 총 6개 노드입니다. Primary 3개인 이유는 Cluster도 다수결로 장애를 판단하기 때문입니다.

Bash — 클러스터 노드 설정 파일 생성
# 각 노드별 디렉토리와 설정 파일 생성
for port in 7000 7001 7002 7003 7004 7005; do
  mkdir -p ./cluster/$port
  cat > ./cluster/$port/redis.conf << EOF
port $port
cluster-enabled yes
cluster-config-file nodes-$port.conf
cluster-node-timeout 5000
appendonly yes
dir ./cluster/$port
EOF
done
Bash — 노드 실행 및 클러스터 생성
# 6개 노드 실행
for port in 7000 7001 7002 7003 7004 7005; do
  redis-server ./cluster/$port/redis.conf &
done

# 클러스터 생성 (--cluster-replicas 1 = 각 Primary마다 1개의 Replica)
redis-cli --cluster create \
  127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
  127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
  --cluster-replicas 1
# 슬롯 분배 확인 후 yes 입력
redis-cli — 클러스터 접속 및 확인
# -c 옵션: 클러스터 모드 (MOVED 자동 리다이렉트)
redis-cli -c -p 7000

# 클러스터 정보
CLUSTER INFO
# cluster_state: ok
# cluster_slots_assigned: 16384

# 슬롯 배분 확인
CLUSTER NODES
# 각 노드의 슬롯 범위 확인

# 키 저장 (자동 리다이렉트)
SET user:1 "alice"
# → Redirected to slot [10485] located at 127.0.0.1:7002
# → OK

# 해시 태그 테스트
SET order:{1000}:item "coffee"
SET order:{1000}:status "pending"
MGET order:{1000}:item order:{1000}:status  # 성공!

⚠️ 실전 팁: Sentinel vs Cluster 선택 기준

기준 Sentinel 추천 Cluster 추천
목적 고가용성(장애 복구) 수평 확장(메모리 부족)
단일 노드 메모리 한계 문제 없음 한계 도달 시
멀티키 명령 필요 자유롭게 사용 가능 해시 태그 필요 (제약 있음)
운영 복잡도 낮음 높음
최소 노드 수 3 (Sentinel) + 2 (Redis) 6 (3Primary + 3Replica)
데이터 양 수십 GB 이하 수십 GB 초과 시

🚨 Cluster의 제약사항

멀티키 명령 제한: 서로 다른 슬롯의 키를 한 번에 조작하는 MGET, MSET, SUNIONSTORE 등은 오류가 납니다. 해시 태그로 같은 슬롯에 모으거나, 애플리케이션에서 개별 요청으로 처리해야 합니다.

데이터베이스 번호 제한: Cluster에서는 SELECT 명령으로 DB 번호를 바꿀 수 없습니다. 오직 DB 0만 사용 가능합니다.

🐍 Python으로 Cluster 연결

Python
# pip install redis[hiredis]  (redis-py에 Cluster 지원 내장)
from redis.cluster import RedisCluster

# Cluster 클라이언트 생성
rc = RedisCluster(
    host="127.0.0.1",
    port=7000,
    decode_responses=True,
    skip_full_coverage_check=True
)

# 일반 명령 (자동 리다이렉트)
rc.set("user:1", "alice")
print(rc.get("user:1"))  # → "alice"

# 해시 태그로 MSET/MGET
rc.mset({
    "order:{1000}:item": "coffee",
    "order:{1000}:status": "pending",
    "order:{1000}:qty": "2"
})
values = rc.mget("order:{1000}:item", "order:{1000}:status", "order:{1000}:qty")
print(values)  # → ['coffee', 'pending', '2']

# 클러스터 정보
print(rc.cluster_info())

📌 11편 핵심 요약

  1. 16,384 해시 슬롯: 모든 키는 CRC16 % 16384 슬롯에 배정
  2. MOVED 리다이렉트: 엉뚱한 노드에 요청하면 올바른 노드로 안내 (-c 옵션으로 자동)
  3. 해시 태그 {}: 관련 키를 같은 슬롯에 모아 멀티키 명령 가능
  4. 최소 6노드: 3 Primary + 3 Replica
  5. Sentinel vs Cluster: HA vs 수평 확장 — 목적에 따라 선택

✅ 11편 체크리스트

  • 16,384 슬롯과 CRC16 해시를 이용한 키 배분 방식을 이해했다
  • redis-cli -c로 클러스터에 접속하고 MOVED 리다이렉트를 확인했다
  • 해시 태그 {} 없이 MGET이 실패하는 것과, 있으면 성공하는 것을 확인했다
  • CLUSTER INFO, CLUSTER NODES로 클러스터 상태를 확인했다
  • Sentinel과 Cluster의 선택 기준을 설명할 수 있다
  • Cluster에서 SELECT 명령을 쓸 수 없다는 제약을 알고 있다

🧠 퀴즈 3문항

Q1. Redis Cluster에서 총 슬롯 수는 몇 개인가요?
🎰 16,384개입니다. 이 슬롯이 여러 Primary 노드에 균등 분배됩니다.
Q2. 해시 태그 order:{1000}:item 에서 슬롯 계산에 사용되는 부분은?
🏷️ {} 안의 1000만 사용됩니다. slot = CRC16("1000") % 16384로 계산합니다. 이를 통해 order:{1000}:item, order:{1000}:status 등이 모두 같은 슬롯에 저장됩니다.
Q3. 메모리 부족이 문제라면 Sentinel을 쓰나요, Cluster를 쓰나요?
📦 Cluster입니다. Sentinel은 고가용성(장애 복구)을 위한 것이지 메모리 확장을 위한 것이 아닙니다. 단일 노드 메모리 한계를 넘어서야 한다면 Cluster로 수평 확장해야 합니다.

📝 과제 3가지

  1. 슬롯 확인: redis-cli -c -p 7000 CLUSTER KEYSLOT user:1000으로 특정 키의 슬롯 번호를 확인하고, 어떤 노드가 그 슬롯을 담당하는지 CLUSTER NODES와 비교하세요.
  2. 노드 추가: 새 노드를 실행하고 CLUSTER MEET으로 클러스터에 합류시킨 후, CLUSTER RESHARD로 슬롯 일부를 이전해 보세요.
  3. 장애 시뮬레이션: Primary 노드 하나를 종료하고 Replica가 자동으로 승격되는지 CLUSTER NODES로 확인하세요.