백엔드 데이터 인프라 68편. Redis Cluster — 데이터 샤딩으로 단일 인스턴스 메모리 한계 돌파. 16384 슬롯·CRC16·hash tag·gossip protocol·자동 failover·CROSSSLOT 제한·MOVED/ASK redirection까지 풀어쓴 학습 노트.
이 글은 백엔드 데이터 인프라 시리즈 130편 중 68편이에요. 67편 에서 Master-Replica 복제로 읽기 분산과 기본 HA(High Availability, 고가용성)를 잡았다면, 이번 68편은 Cluster — 쓰기와 메모리도 분산해서 단일 인스턴스 한계를 돌파해요. Redis 운영에서 가장 큰 결정.
Redis Cluster가 어렵게 느껴지는 이유
Cluster 는 Redis 에서 가장 복잡한 영역이에요. 막히는 자리가 셋.
첫째, 16384 슬롯·CRC16·hash tag 같은 새 용어가 한꺼번에 쏟아져요. CRC16(Cyclic Redundancy Check, 16비트 해시 함수) 같은 게 분산 시스템의 일반 개념이 아니라 Redis 특유의 구체 메커니즘이라 학습 곡선이 있어요.
둘째, CROSSSLOT 제한(여러 키가 서로 다른 슬롯에 있을 때 나는 오류). 여러 키에 걸친 명령이 모두 같은 슬롯에 있어야만 동작해서 MSET·MGET·SINTER·MULTI/EXEC·Lua 가 다 영향받아요. 단일 인스턴스에서 잘 돌던 코드가 Cluster 환경으로 옮긴 순간 갑자기 깨지는 사고가 여기서 나요.
셋째, 자동 failover 메커니즘을 이해해야 해요. Master 가 죽으면 replica 가 자동으로 promote 되는데, 그 안에 gossip protocol(노드끼리 메타데이터를 주고받는 방식)·quorum(과반수 동의)·split-brain(클러스터가 둘로 갈리는 현상) 같은 분산 시스템의 깊은 주제가 얽혀 있어요.
이 글에서 Cluster 핵심 7가지(슬롯·CRC16·hash tag·MOVED·ASK·gossip·failover)와 CROSSSLOT 회피 패턴, 그리고 운영 결정 가이드까지 한 번에 풀어요.
핵심 모델 — 16384 슬롯
+---------+ +---------+ +---------+
| Node A | | Node B | | Node C |
|slot 0- | |5462- | |10923- |
| 5461 | |10922 | |16383 |
+---------+ +---------+ +---------+
\ | /
\ gossip protocol /
전체 키 공간이 16384 슬롯 (0~16383)으로 나뉘고, 각 노드가 연속된 슬롯 범위를 담당해요.
CRC16 — 슬롯 계산
slot = CRC16(key) % 16384
키 이름을 CRC16 으로 해시하고 16384 로 나눈 나머지가 슬롯 번호예요. 이 번호로 어느 노드에 가는지 결정돼요.
> CLUSTER KEYSLOT user:42
(integer) 5474 # user:42 는 슬롯 5474 → Node B
Replication 와의 관계
Cluster 안의 각 마스터 노드가 자신만의 replica 를 가질 수 있어요.
Node A (master) ←→ Node A' (replica)
Node B (master) ←→ Node B' (replica)
Node C (master) ←→ Node C' (replica)
총 6개 인스턴스 = 3 master + 3 replica. Master 가 죽으면 해당 replica 가 자동으로 promote 돼요.
Hash Tag — CROSSSLOT 회피
여기가 가장 자주 사고나는 자리예요.
문제
> MGET user:42 user:99
(error) CROSSSLOT Keys in request don't hash to the same slot
user:42 와 user:99 가 서로 다른 슬롯에 있어서 한 명령으로 못 가져와요.
해결 — {} Hash Tag
키 이름 중 {} 안의 부분만 CRC16 해시 대상이에요.
> MGET {user42}:name {user42}:email {user42}:address
1) "Alice"
2) "alice@..."
3) "..."
{user42} 가 공통 hash tag(키 일부를 묶어 같은 슬롯으로 보내는 표시) 가 돼서 세 키 모두 같은 슬롯에 떨어져요.
응용
- MULTI/EXEC = 안의 모든 키 같은 슬롯 필요 → hash tag
- Lua script KEYS = 모두 같은 슬롯 → hash tag
- SINTER·SUNION·SDIFF = 모든 입력 키 같은 슬롯 → hash tag
- SORT BY = 외부 키 참조 시 같은 슬롯
함정
여기서 시험 함정이 하나 — hash tag 가 너무 많은 키를 같은 슬롯에 모으는 경우예요. 같은 사용자의 모든 키를 {user42}: prefix 로 묶었는데 그 사용자가 hot user(트래픽이 몰리는 사용자) 면 해당 노드에만 부하가 집중돼요. 이걸 hot slot 이라고 해요.
균형을 잡으려면, 논리적으로 함께 다뤄야 하는 키만 hash tag 로 묶고 나머지는 분산되게 둬요.
MOVED · ASK — Redirection
MOVED — 키가 다른 노드에
client → Node A: GET user:99
Node A 가 알림: user:99 는 슬롯 7000 → Node B 에 있음
Node A → client: (error) MOVED 7000 192.168.1.2:6379
client 가 Node B 로 재시도
스마트 클라이언트는 MOVED 를 받으면 자동으로 redirect 하고 슬롯 맵 캐시까지 업데이트해요. 다음 요청부터는 바로 Node B 로 가요.
ASK — Resharding 중
슬롯이 옮겨지는 중(resharding, 슬롯 재분배)이라 임시로 다른 노드에 있는 상태:
client → Node A: GET key
Node A → client: ASK 7000 192.168.1.2:6379
client → Node B: ASKING (먼저)
client → Node B: GET key
ASKING 명령으로 임시 허용 표시를 먼저 보내고 실제 명령을 날려요. MOVED 와 달리 영구 redirect 가 아니에요.
Gossip Protocol — 노드 간 통신
각 노드가 주기적으로 다른 노드들과 메타데이터를 교환해요. cluster bus(master 포트 + 10000 의 별도 포트) 를 써요.
교환 정보:
- 노드 상태 (살아 있는지·응답 시간)
- 슬롯 맵 (어떤 슬롯이 어느 노드에)
- 실패 감지 (
PING응답 안 오면 PFAIL(Possible Fail, 의심 실패) → FAIL(확정 실패) 마킹)
Failure Detection
1. Node A 가 Node B 에 PING 보냄
2. 일정 시간 (cluster-node-timeout, 기본 15초) 응답 X
3. Node A 가 Node B 를 PFAIL (Possible Fail) 마킹
4. Node A 가 다른 노드들에 PFAIL 정보 gossip
5. 다른 노드들도 같은 의견이면 → 다수결로 FAIL 확정
6. Node B 의 replica 중 하나가 자동 promote
Quorum 은 master 의 과반수가 동의해야 failover 가 진행되는 규칙이에요. split-brain 을 막아줘요.
자동 Failover
Master 죽음을 감지한 뒤:
- Election — replica 들이 자신을 master 로 뽑아달라고 투표 요청
- Quorum 확인 — 살아 있는 master 의 과반수 동의
- Promote — 가장 최신 데이터를 가진 replica 가 새 master
- Slot 인수 — 죽은 master 의 슬롯 인수
- Gossip — 모든 노드에 새 상태 broadcast
전체 5~30초 정도. 클라이언트 측에서는 짧은 에러 응답 뒤 자동으로 재시도해요.
CROSSSLOT 영향 — 코드 변경 자주
여기까지 따라오셨다면 의문이 하나 떠올라요 — "기존 코드를 Cluster 로 옮기면 뭐가 깨지나?". 잘 깨지는 자리:
| 명령 | 단일 인스턴스 | Cluster |
|---|---|---|
MSET k1 v1 k2 v2 |
OK | 같은 슬롯이어야 |
MGET k1 k2 |
OK | 같은 슬롯이어야 |
SINTER s1 s2 |
OK | 같은 슬롯이어야 |
MULTI/EXEC 다중 키 |
OK | 같은 슬롯이어야 |
EVAL 여러 KEYS |
OK | 모두 같은 슬롯이어야 |
SORT key BY external |
OK | 외부 키도 같은 슬롯 |
해법 패턴은 hash tag 도입이에요.
Before:
r.mset({'user:42:name': 'Alice', 'user:42:email': 'alice@...'})
After:
r.mset({'{user:42}:name': 'Alice', '{user:42}:email': 'alice@...'})
또는 명령 자체를 분할:
pipe = r.pipeline(transaction=False)
pipe.set('user:42:name', 'Alice')
pipe.set('user:42:email', 'alice@...')
pipe.execute() # Pipelining 은 다른 슬롯 OK (각각 라우팅)
Cluster vs 단일 인스턴스 vs Sentinel — 결정 가이드
| 옵션 | 메모리 | 쓰기 분산 | 자동 Failover | 운영 복잡도 |
|---|---|---|---|---|
| 단일 인스턴스 | 단일 노드 한계 | X | X | 낮음 |
| 단일 + Sentinel (69편) | 단일 노드 한계 | X | ◯ | 중간 |
| Cluster | 분산 | ◯ | ◯ | 높음 |
선택
- 메모리 < 30GB + 단순 환경 → 단일 인스턴스 + Replication
- 메모리 < 30GB + HA 필요 → Sentinel (69편)
- 메모리 > 30GB 또는 쓰기 TPS(Transactions Per Second, 초당 처리량) 매우 큼 → Cluster
"무조건 Cluster" 는 함정이에요. Cluster 운영 복잡도가 단일 인스턴스 + Sentinel 보다 훨씬 커서, 진짜 필요할 때만 골라요.
클라이언트 지원
Cluster 환경에서는 Cluster-aware 클라이언트(클러스터 라우팅을 이해하는 라이브러리) 가 필요해요.
- Python —
redis-py-cluster또는 redis-py 4.0+ 의RedisCluster - Java — Jedis
JedisCluster, LettuceRedisClusterClient - Spring Data Redis —
LettuceClusterConfiguration
from redis.cluster import RedisCluster
rc = RedisCluster(host="cluster-node-1", port=6379)
rc.set("user:42", "Alice") # 자동 라우팅 + MOVED 핸들링
한계·실무 함정
1. Cluster 안에서 multi-key 명령 제한
위에서 본 CROSSSLOT 함정. 기존 코드 마이그레이션할 때 작업량이 가장 크게 잡혀요.
2. Pub/Sub broadcast 부담
58편에서 본 대로 전통 Pub/Sub 은 모든 노드에 broadcast 돼요. 큰 클러스터에서는 내부 네트워크 부담이 커져요. Sharded Pub/Sub (Redis 7+) 으로 해결.
3. Resharding 운영
노드를 추가하거나 제거할 때 슬롯 재분배가 필요해요. 온라인 작업이지만 I/O 부담이 있어요. redis-cli --cluster reshard 도구를 써요.
4. 데이터 분포 불균등 (hot slot)
대부분의 키가 같은 hash tag 또는 특정 슬롯에 집중되면 그 노드만 부하를 받아요. 모니터링이 필요해요.
5. 클라이언트 라이브러리 버전
오래된 클라이언트는 Cluster 미지원이거나 MOVED 자동 처리가 안 돼요. 도입 전에 클라이언트 버전부터 확인.
시험 직전 한 번 더 — Redis Cluster 함정 압축 노트
- Redis Cluster = 데이터 샤딩 + 자동 failover
- 16384 슬롯 = 전체 키 공간 (0~16383)
- 각 노드 = 연속 슬롯 범위 담당
- CRC16(key) % 16384 = 키의 슬롯 번호
CLUSTER KEYSLOT key= 슬롯 조회- 각 마스터 = 자신만의 replica 보유 가능
- Master 죽음 → replica 자동 promote
- Hash Tag
{}={}안 부분만 CRC16 → 여러 키 같은 슬롯 {user42}:name·{user42}:email= 같은 슬롯- 함정 = 너무 많은 키 같은 슬롯 → hot slot
- MOVED redirection = 영구 (스마트 클라이언트 자동 재시도)
- ASK redirection = resharding 임시 (
ASKING먼저) - Gossip protocol = 노드 간 메타데이터 교환 (cluster bus 포트 = master + 10000)
- PFAIL → FAIL = 다수결 실패 감지
- Quorum = master 과반수 동의 → split-brain 방지
- 자동 failover = ~5~30초
- CROSSSLOT 영향 = MSET·MGET·SINTER·MULTI·EVAL 등 다중 키 명령
- 해결 = hash tag 또는 Pipelining 분할
- 결정 가이드 — <30GB + 단순 = 단일 / <30GB + HA = Sentinel / >30GB or 쓰기 분산 = Cluster
- Cluster 운영 복잡도 = Sentinel 보다 훨씬 큼
- 클라이언트 = Cluster-aware 필요 (redis-py-cluster, JedisCluster, LettuceClusterClient)
- 함정 — Pub/Sub broadcast = Sharded Pub/Sub (7+)
- 함정 — resharding I/O 부담 → 트래픽 적은 시간
- 함정 — hot slot 모니터링 필수
- 함정 — 오래된 클라이언트 = Cluster 미지원
공식 문서: Redis Cluster 에서 자세한 사양과 운영 가이드를 확인할 수 있어요.
시리즈 다른 편 (앞뒤 글 모음)
이전 글:
- 63편 — Distributed Lock + Redlock 알고리즘
- 64편 — Twitter Clone 패턴 + Fanout 전략
- 65편 — Secondary Indexing 패턴 (RDB 인덱스 모방)
- 66편 — Redis Persistence (RDB · AOF · Hybrid)
- 67편 — Redis Replication (Master-Replica)
다음 글: