Consistent Hashing
일관성 해싱
🎯 일관성 해싱 (보강 버전 Part 1)
1. 왜 해싱이 필요할까?
서버가 여러 대 → 데이터를 나눠 담아야 함
단순 방식:
key % 서버개수(예: 손님 번호 % 마트 개수)문제: 서버 하나 추가/삭제 시 전체 데이터 재분배 발생
실제로 수백만 건 데이터가 전부 흔들리면 → 서비스 멈춤/대규모 지연 위험 🚨
운영 환경에서는 거의 불가능한 방식
2. 해시 링(Hash Ring) 비유
원형 시계판에 서버 = 마트, 데이터 = 손님을 배치
손님은 자기보다 시계방향으로 가장 가까운 마트로 이동
해시 함수: 데이터 → 숫자 → 링 위 위치 변환
장점: 구조가 단순하면서도 유연하고 안정적
3. 노드 추가/삭제 시 변화
새 서버 등장 → 해당 구간 손님만 새 서버로 이동
서버 하나 제거 → 그 서버 손님만 옆 서버로 이동
전체 재분배 ❌, 일부 이동 ⭕
결과: 확장성·장애 대응력이 뛰어남
운영 관점: 서버 증설 시에도 서비스 중단이 거의 없음
4. 가상 노드(Virtual Node)
현실: 서버 성능·용량은 제각각
단순 배치 시 특정 서버에 데이터 몰려 Hotspot(핫스팟) 발생
해결책: 하나의 물리 서버를 여러 가상 노드로 쪼개서 링에 흩뿌림
마치 대형 마트를 작은 체인점 여러 개로 나눠 동네 곳곳에 배치하는 느낌
효과: 데이터 분산 균형 ↑, 병목 ↓
5. 실제 서비스 활용
Redis Cluster: 키 → CRC16 해시 → 16384 슬롯 → 서버 매핑
Cassandra: 파티션 키 → 토큰 링 → 노드 분산
Memcached: 서버 증설/삭제에도 데이터 최소 이동으로 안정 운영
공통점: “일관성 해싱 원리”를 각 서비스에 맞게 변형
결론: 단순한 이론이 아니라 실무 핵심 기술
6. 개발자가 얻는 이점
서버 확장(스케일 아웃)이 두렵지 않음
장애 발생 시 전체 재분배가 아닌 일부만 이동
데이터가 고르게 퍼져 특정 서버 병목 방지
운영 비용 절감 + 서비스 안정성 확보
실무에서 “캐시, 세션, DB 파티셔닝” 문제를 풀어주는 열쇠
7. 단점도 있다
완벽히 균등 분산 ❌ → 여전히 Hotspot 가능
해시 함수 선택에 따라 성능 차이 발생 (예: SHA1 vs MurmurHash)
가상 노드 설계/운영이 추가 관리 포인트
모니터링 없으면 특정 노드 과부하를 놓치기 쉬움
즉, “도입만 하면 끝”은 아님 → 운영 지식 필요
8. 대안 알고리즘도 있다
Rendezvous Hashing(HRW): 키마다 모든 서버와 점수 계산 → 최고 점수 서버 선택, 구현 단순
Weighted Hashing: 서버 성능에 맞춰 가중치 적용 가능
Jump Consistent Hash: 최근 등장, 계산 빠르고 균형 분배 우수
상황별로 장단점 존재 → 서비스 특성에 맞는 선택 필요
9. 그림으로 정리하기
해시 링 기본 구조: 서버와 데이터가 원형에 배치
노드 추가/삭제 전후: 일부만 이동하는 모습 강조
가상 노드: 큰 서버가 여러 점으로 흩어져 있는 그림
Hotspot 예시: 특정 구간에 데이터가 몰려 과부하 발생
시각 자료가 있으면 초보자도 직관적으로 이해 가능
10. 마무리: 이걸 알면 뭐가 좋을까?
일관성 해싱은 “확장성과 유연성”을 제공하는 핵심 아이디어
단순 수학 문제가 아니라 대규모 서비스 운영 필수 지식
캐시, 세션 저장소, DB 파티셔닝, 로드밸런서 설계 등 광범위하게 활용됨
실무 메시지: 규모가 커지는 순간 반드시 마주치는 문제 → 알아두면 필살기
11. Redis Cluster의 슬롯 개념
Redis는 데이터를 16,384개의 슬롯(slot) 으로 쪼갬
키 →
CRC16 해시→mod 16384→ 슬롯 번호 결정각 슬롯은 특정 노드가 맡아서 저장/관리
노드가 추가/삭제돼도 → 슬롯만 재배치하면 됨
👉 “큰 창고를 작은 칸으로 나눠두면, 옮길 때 일부 칸만 움직이면 되는 구조”
12. 슬롯과 일관성 해싱 비교
Consistent Hashing: 원형 링 위에서 노드 배치 → 유연하지만 구현 복잡
Redis Cluster: 미리 정해진 슬롯 16,384개 → 단순, 예측 가능
장점: 리밸런싱 범위가 명확, 관리 쉬움
단점: 슬롯 수는 고정 → 동적 확장 불가
👉 “원형 링(자유로운 지도)” vs “칸 나눠진 룰렛판(정해진 틀)”
13. 노드 추가/삭제 시 슬롯 재분배
새 노드 추가 → 기존 노드가 가진 일부 슬롯만 양도
노드 삭제 → 그 노드 슬롯을 다른 노드가 인수
전체 데이터 이동 ❌, 슬롯 단위 일부 이동 ⭕
👉 “마트 점포가 늘어나면 주변 물건 몇 박스만 새 점포로 옮기는 느낌”
14. 마스터-슬레이브 구조와 고가용성
각 슬롯 = 기본적으로 마스터 노드 담당
마스터마다 슬레이브 노드(복제본) 붙음
마스터 장애 → 슬레이브 자동 승격
데이터 손실은 최소화, 서비스는 계속 진행
👉 “마트 본점(마스터) + 지점(슬레이브). 본점이 닫아도 지점이 바로 가게 열어줌”
15. 데이터 조회 시 해시 태그(Hash Tag)
키 안에
{}를 쓰면 그 안 문자열만 해시 계산예:
{user:1000}:name,{user:1000}:tweets→ 같은 슬롯으로 묶임덕분에 멀티키 연산/트랜잭션 가능
👉 “같은 박스에 담아놔야 같이 꺼낼 수 있음”
16. 클라이언트-서버 간 키 라우팅
클라이언트가 직접 키 → 슬롯 → 노드 계산
클러스터 구성이 바뀌면 서버가
MOVED,ASK신호를 보내 새 노드 알려줌클라이언트는 다시 요청을 재전송 → 최신 구조 따라감
👉 “택배 기사님이 이사 간 집 주소 알려주는 안내판”
17. 실제 장애 상황 시 동작
노드 다운 → 해당 슬롯의 슬레이브가 자동 승격
잠깐의 지연은 있을 수 있지만, 데이터 유실 최소화
심각한 장애 → 관리자가
redis-cli cluster로 수동 조정👉 “자동 대체 시스템이 있지만, 최악의 경우 매니저가 직접 개입”
18. Redis Cluster의 장단점
장점
구조 단순, 관리 예측 가능
빠른 확장성과 높은 가용성
일관성 해싱의 장점을 실제로 구현
단점
슬롯 개수 고정(16384) → 동적 확장 ❌
멀티키 연산은 같은 슬롯에서만 가능
특정 슬롯에 트래픽 몰리면 핫스팟 문제 발생
👉 “깔끔한 룰렛판이지만, 룰렛판 크기를 바꾸긴 힘듦”
19. Redis Cluster vs Consistent Hashing 총정리
공통점: 노드 변경 시 전체가 아닌 일부만 이동
차이점
Consistent Hashing → 원형 링, 유연함
Redis Cluster → 고정 슬롯, 단순/관리 쉬움
Redis가 슬롯 방식을 택한 이유: 개발자·운영자 입장에서 실용적
👉 “철학은 같지만, 구현은 현실적으로 간소화”
20. 실무 적용 인사이트
Redis Cluster는 캐시/세션 저장소로 많이 사용
Hash Tag 설계를 잘못하면 → 멀티키 연산 불가 → 큰 문제 발생
장애 대응, 슬롯 재분배 전략을 사전에 연습해 두는 게 중요
결론: Redis Cluster는 단순 DB가 아니라 확장성과 안정성을 갖춘 분산 설계 도구
👉 “그냥 창고가 아니라, 확장 계획까지 세워둔 스마트 창고”
Redis Cluster: docker-compose로 5노드 시작 → 노드 추가/삭제 튜토리얼
0) 구성 개요
노드 수: 5 (마스터 3 + 슬레이브 2)로 시작
포트: 7001~7005 (각 컨테이너 내부 6379에 매핑)
클러스터 생성:
redis-cli --cluster create ... --cluster-replicas 1확장(추가): 새 컨테이너 띄운 뒤
add-node→ (원하면) 리밸런싱(reshard)축소(삭제): 대상 노드의 슬롯을 다른 노드로 옮긴 뒤
del-node
1) docker-compose.yml (5노드 시작)
Bitnami 이미지로 간단하게. 컨테이너 호스트네임을 클러스터에 알리도록 설정해 재분배/리다이렉트가 잘 동작하게 함.
참고
비밀번호가 필요하면
REDIS_PASSWORD=yourpass를 모든 노드에 동일하게 지정하고, 아래redis-cli호출 시-a yourpass추가.로컬 포트를 700x로 나눈 건 보기 편하라고 한 것. 내부 포트는 항상 6379다.
2) 컨테이너 기동 & 클러스터 생성
위 명령이 끝나면 마스터 3개 + 슬레이브 2개로 자동 구성된다.
상태 확인:
3) (선택) 슬롯 분포/건강 체크
cluster slots로 슬롯 범위(0~16383)가 어떤 마스터에 할당됐는지 확인.마스터 각각에 5~6천 슬롯 정도가 분산돼 있으면 정상.
4) 노드 추가(확장): 5 → 6 노드
4-1) compose에 새 서비스 추가
4-2) 클러스터에 노드 합류(add-node)
이 시점엔 슬롯을 아직 받지 않은 빈 마스터 상태.
4-3) 리밸런싱(슬롯 재분배, reshard)
결과: 기존 마스터들 일부 슬롯이 redis-node-6으로 이동.
팁
“새 노드를 슬레이브(복제본)로만 추가”하고 싶다면:
add-node로 일단 추가
redis-cli --cluster replicate <new-node-id> <master-node-id>실행
5) 노드 삭제(축소): 6 → 5 노드
핵심: 해당 노드의 슬롯을 먼저 다른 마스터로 모두 옮긴 후
del-node.
5-1) 슬롯 비우기(reshard로 슬롯 이동)
5-2) del-node (클러스터에서 제거)
5-3) 컨테이너 정리
주의
삭제하려는 노드가 마스터라면 반드시 슬롯 0개 상태에서 del-node.
삭제하려는 노드가 슬레이브라면, 먼저
cluster nodes에서 master/slave 관계를 확인하고, 필요 시 다른 슬레이브로 재배치.
6) 장애/Failover 빠른 연습
슬레이브가 같은 슬롯을 이어받으면 정상.
복구 후:
7) Spring 연결 포인트(요약)
이미 스프링 설정은 진행 중이니, 클러스터 호스트는 서비스명:6379로 쓰면 깔끔.
추가/삭제 후에도 Lettuce가
MOVED/ASK를 따라가며 토폴로지를 갱신.해시 태그(
{user:42})로 멀티키 연산을 같은 슬롯에 묶는 패턴은 그대로 유지.
8) 운영 팁 (요약 체크리스트)
추가: 컨테이너 띄우기 →
add-node→ (필요 시)rebalance삭제:
rebalance/reshard로 슬롯 0 만들기 →del-node→ 컨테이너 제거모니터링:
cluster info,cluster nodes, Prometheus + Grafana보안:
REDIS_PASSWORD통일 + TLS(필요 시), ACL백업: RDB/AOF, 스냅샷 경로 볼륨 마운트 권장
Spring 애플리케이션에서 Consistent Hashing/Redis Cluster 쓰는 법
1) 키 네이밍 & Hash Tag 유틸 (슬롯 고정)
멀티키/트랜잭션을 쓸 가능성이 있는 도메인은 반드시 Hash Tag로 묶자:
{user:42}:profile
2) RedisTemplate 기반 Repository (Value/Hash)
직렬화는
StringRedisSerializer+GenericJackson2JsonRedisSerializer추천(이미 설정했다고 가정).
3) 멀티키 조회(MGET) & 파이프라인 (같은 슬롯에 묶어야 함)
같은
{user:42}태그로 묶이면 클러스터에서도 안전하게 배치/파이프라인 가능.
4) Spring Cache (+ Hash Tag 포함 KeyGenerator)
캐시 키에도 태그를 포함시켜 같은 슬롯 유지.
5) 레이트리미터(Token Bucket) – Lua (원자적)
클러스터에서 Lua/EVAL은 같은 슬롯 키만 안전. 키를 태그로 묶자.
6) 분산락(선택: Redisson) – 임계 구역 보호
Redisson이면 코드가 간단해짐. (Lettuce로 SET NX 구현도 가능하지만 Redisson이 안전장치 풍부)
7) 세션 공유 (Spring Session)
Sticky Session 없이도 세션 공유. (Hash Tag는 필요 없음)
8) 멀티키 트랜잭션(동일 슬롯 전제) & TxCallback
SessionCallback으로 MULTI/EXEC. 키들은 같은 태그로!
9) 장애/리밸런싱 중 리다이렉션 & 재시도
Lettuce는
MOVED/ASK자동 처리 + 토폴로지 리프레시.네트워크/타임아웃 재시도는 Spring Retry로 보완.
10) 관측성: 캐시 미스/Latency 메트릭
Micrometer로 간단히 지표 남기기.
11) 통합 테스트 (Testcontainers로 클러스터 연결 확인)
단일 RedisContainer로도 연결 검증은 가능. (로컬에 이미 클러스터가 있다면 생략)
12) 성능 팁 & 체크리스트 (짧게)
키 설계: 반드시 Hash Tag로 멀티키/Tx 가능한 그룹 정의
직렬화 비용: 큰 객체라면 JSON 대신 바이너리(FST/Smile) 고려
TTL 전략: 핫키는 짧게, 백오프 재시도 시 TTL 재설정 주의
파이프라인: 읽기/쓰기 벌크 처리 시
executePipelined적극 활용핫스팟: 특정 태그에 과도한 트래픽이면 태그 스키마(샤딩 태그 추가)로 분산
끝으로
핵심 1: 키를 태그로 “묶는 설계”가 클러스터에서 모든 걸 결정한다.
핵심 2: Lua/멀티키/트랜잭션/락은 같은 슬롯이 전제.
핵심 3: 장애/리밸런싱은 클라이언트가 따라가므로, 타임아웃/재시도/모니터링만 잘 세팅하면 실무 내구성 충분.
Last updated