분산 캐시 시스템
분산 캐시 시스템¶
개요¶
이 문서에서는 분산 캐시 시스템의 핵심 개념을 다룹니다. Redis의 데이터 구조와 클러스터 구성, Memcached와의 비교, 그리고 일관성 해싱(Consistent Hashing)을 학습합니다.
난이도: ⭐⭐⭐ 예상 학습 시간: 2-3시간 선수 지식: 06_Caching_Strategies.md
목차¶
1. 분산 캐시란?¶
1.1 분산 캐시의 필요성¶
┌─────────────────────────────────────────────────────────────────┐
│ 분산 캐시의 필요성 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 로컬 캐시의 문제: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [Server 1] [Server 2] [Server 3] │ │
│ │ Local Cache Local Cache Local Cache │ │
│ │ user:123 ✓ user:123 ✗ user:123 ✗ │ │
│ │ │ │
│ │ 문제: │ │
│ │ 1. 각 서버의 캐시가 다름 (불일치) │ │
│ │ 2. 메모리 낭비 (같은 데이터 중복) │ │
│ │ 3. 캐시 무효화 어려움 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 분산 캐시 적용: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [Server 1] [Server 2] [Server 3] │ │
│ │ │ │ │ │ │
│ │ └──────────────┼──────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌────────────────────┐ │ │
│ │ │ Distributed │ │ │
│ │ │ Cache (Redis) │ │ │
│ │ │ user:123 ✓ │ │ │
│ │ └────────────────────┘ │ │
│ │ │ │
│ │ 장점: │ │
│ │ 1. 모든 서버가 같은 캐시 공유 │ │
│ │ 2. 캐시 일관성 유지 │ │
│ │ 3. 중앙 집중식 관리 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
1.2 로컬 캐시 vs 분산 캐시¶
| 항목 | 로컬 캐시 | 분산 캐시 |
|---|---|---|
| 저장 위치 | 애플리케이션 메모리 | 별도 서버 (Redis 등) |
| 속도 | 매우 빠름 (ns~us) | 빠름 (ms) |
| 용량 | 서버 메모리 제한 | 확장 가능 |
| 일관성 | 서버별 다름 | 공유/일관 |
| 장애 | 서버 재시작 시 손실 | 독립적 관리 가능 |
| 사용 예 | 단일 서버, 읽기 전용 | 다중 서버, 세션 등 |
2. Redis 데이터 구조¶
2.1 String¶
┌─────────────────────────────────────────────────────────────────┐
│ String │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 가장 기본적인 키-값 저장 │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ SET user:123:name "John" │ │
│ │ GET user:123:name → "John" │ │
│ │ │ │
│ │ # 만료 시간 설정 (초) │ │
│ │ SET session:abc "data" EX 3600 │ │
│ │ SETEX session:abc 3600 "data" # 동일 │ │
│ │ │ │
│ │ # 원자적 증가/감소 │ │
│ │ SET counter 0 │ │
│ │ INCR counter → 1 │ │
│ │ INCRBY counter 10 → 11 │ │
│ │ DECR counter → 10 │ │
│ │ │ │
│ │ # NX (Not eXists): 키가 없을 때만 설정 │ │
│ │ SET lock:resource "owner" NX EX 30 # 분산 락 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 사용 사례: │
│ • 세션 저장 │
│ • 캐시 (JSON 직렬화) │
│ • 카운터 (조회수, 좋아요) │
│ • 분산 락 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.2 Hash¶
┌─────────────────────────────────────────────────────────────────┐
│ Hash │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 필드-값 쌍의 컬렉션 (객체 저장에 적합) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # 사용자 객체 저장 │ │
│ │ HSET user:123 name "John" age 30 email "john@example.com" │ │
│ │ │ │
│ │ # 단일 필드 조회 │ │
│ │ HGET user:123 name → "John" │ │
│ │ │ │
│ │ # 전체 조회 │ │
│ │ HGETALL user:123 │ │
│ │ → {"name": "John", "age": "30", "email": "..."} │ │
│ │ │ │
│ │ # 필드 증가 │ │
│ │ HINCRBY user:123 age 1 → 31 │ │
│ │ │ │
│ │ # 필드 존재 확인 │ │
│ │ HEXISTS user:123 name → 1 │ │
│ │ │ │
│ │ 구조: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ user:123 │ │ │
│ │ │ ├── name: "John" │ │ │
│ │ │ ├── age: 30 │ │ │
│ │ │ └── email: "john@example.com" │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 장점 (vs String + JSON): │
│ • 개별 필드 접근/수정 가능 │
│ • 부분 업데이트 효율적 │
│ • 메모리 효율 (작은 해시) │
│ │
└─────────────────────────────────────────────────────────────────┘
2.3 List¶
┌─────────────────────────────────────────────────────────────────┐
│ List │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 순서가 있는 문자열 컬렉션 (Linked List) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # 오른쪽에 추가 (Queue 용도) │ │
│ │ RPUSH queue:jobs "job1" "job2" "job3" │ │
│ │ │ │
│ │ # 왼쪽에서 꺼내기 │ │
│ │ LPOP queue:jobs → "job1" │ │
│ │ │ │
│ │ # 왼쪽에 추가 (Stack 용도) │ │
│ │ LPUSH stack:items "item1" │ │
│ │ LPOP stack:items → "item1" │ │
│ │ │ │
│ │ # 범위 조회 │ │
│ │ LRANGE queue:jobs 0 -1 → ["job2", "job3"] │ │
│ │ │ │
│ │ # 블로킹 Pop (작업 큐) │ │
│ │ BLPOP queue:jobs 30 # 30초 대기 │ │
│ │ │ │
│ │ 구조: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ queue:jobs │ │ │
│ │ │ [job2] ←→ [job3] │ │ │
│ │ │ Head Tail │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 사용 사례: │
│ • 작업 큐 (Job Queue) │
│ • 최근 항목 리스트 (최근 본 상품) │
│ • 타임라인 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.4 Set¶
┌─────────────────────────────────────────────────────────────────┐
│ Set │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 중복 없는 문자열 컬렉션 (순서 없음) │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # 멤버 추가 │ │
│ │ SADD user:123:followers "user:456" "user:789" │ │
│ │ │ │
│ │ # 멤버 확인 │ │
│ │ SISMEMBER user:123:followers "user:456" → 1 │ │
│ │ │ │
│ │ # 전체 멤버 │ │
│ │ SMEMBERS user:123:followers → {"user:456", "user:789"} │ │
│ │ │ │
│ │ # 집합 연산 │ │
│ │ SINTER user:123:followers user:456:followers # 교집합 │ │
│ │ SUNION user:123:followers user:456:followers # 합집합 │ │
│ │ SDIFF user:123:followers user:456:followers # 차집합 │ │
│ │ │ │
│ │ # 개수 │ │
│ │ SCARD user:123:followers → 2 │ │
│ │ │ │
│ │ 구조: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ user:123:followers │ │ │
│ │ │ { "user:456", "user:789" } │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 사용 사례: │
│ • 태그 │
│ • 팔로워/팔로잉 │
│ • 좋아요 사용자 목록 │
│ • 고유 방문자 │
│ │
└─────────────────────────────────────────────────────────────────┘
2.5 Sorted Set (ZSet)¶
┌─────────────────────────────────────────────────────────────────┐
│ Sorted Set │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 점수(score)로 정렬된 Set │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # 점수와 함께 추가 │ │
│ │ ZADD leaderboard 100 "user:123" 85 "user:456" 92 "user:789"│ │
│ │ │ │
│ │ # 순위별 조회 (오름차순) │ │
│ │ ZRANGE leaderboard 0 2 WITHSCORES │ │
│ │ → [("user:456", 85), ("user:789", 92), ("user:123", 100)] │ │
│ │ │ │
│ │ # 순위별 조회 (내림차순) - 상위 3명 │ │
│ │ ZREVRANGE leaderboard 0 2 WITHSCORES │ │
│ │ → [("user:123", 100), ("user:789", 92), ("user:456", 85)] │ │
│ │ │ │
│ │ # 점수 범위 조회 │ │
│ │ ZRANGEBYSCORE leaderboard 80 95 WITHSCORES │ │
│ │ │ │
│ │ # 순위 조회 │ │
│ │ ZRANK leaderboard "user:123" → 2 (0부터 시작) │ │
│ │ ZREVRANK leaderboard "user:123" → 0 (1등) │ │
│ │ │ │
│ │ # 점수 증가 │ │
│ │ ZINCRBY leaderboard 10 "user:456" → 95 │ │
│ │ │ │
│ │ 구조: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ leaderboard │ │ │
│ │ │ score: 85 → "user:456" │ │ │
│ │ │ score: 92 → "user:789" │ │ │
│ │ │ score: 100 → "user:123" │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 사용 사례: │
│ • 리더보드 │
│ • 우선순위 큐 │
│ • 시간순 타임라인 (score = timestamp) │
│ • Rate Limiting (슬라이딩 윈도우) │
│ │
└─────────────────────────────────────────────────────────────────┘
2.6 데이터 구조 요약¶
| 타입 | 구조 | 시간 복잡도 | 사용 예 |
|---|---|---|---|
| String | 키-값 | O(1) | 캐시, 세션, 카운터 |
| Hash | 필드-값 맵 | O(1) | 객체, 사용자 프로필 |
| List | 연결 리스트 | O(1) Push/Pop | 큐, 최근 항목 |
| Set | 해시셋 | O(1) | 태그, 유니크 방문자 |
| Sorted Set | 스킵 리스트 | O(log N) | 리더보드, 랭킹 |
3. Redis 클러스터와 Sentinel¶
3.1 Redis Sentinel¶
┌─────────────────────────────────────────────────────────────────┐
│ Redis Sentinel │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "Redis 고가용성을 위한 모니터링 및 자동 페일오버" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ Sentinels │ │ │
│ │ │ ┌───────┐ ┌───────┐ ┌───────┐ │ │ │
│ │ │ │Sent 1 │ │Sent 2 │ │Sent 3 │ │ │ │
│ │ │ └───┬───┘ └───┬───┘ └───┬───┘ │ │ │
│ │ │ │ │ │ │ │ │
│ │ └──────┼─────────┼─────────┼─────────┘ │ │
│ │ │ │ │ │ │
│ │ ┌──────┴─────────┴─────────┴──────┐ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Master │ ════복제════════▶ │ Replica │ │ │
│ │ │ (R/W) │ │ (R/O) │ │ │
│ │ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 역할: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. 모니터링: Master/Replica 상태 감시 │ │
│ │ 2. 알림: 장애 발생 시 관리자에게 알림 │ │
│ │ 3. 자동 페일오버: Master 장애 시 Replica를 Master로 승격 │ │
│ │ 4. 설정 제공: 클라이언트에게 현재 Master 주소 제공 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 페일오버 과정: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 1. Master 장애 감지 (다수 Sentinel 동의) │ │
│ │ 2. 하나의 Sentinel이 리더로 선출 │ │
│ │ 3. 리더가 Replica 중 하나를 Master로 승격 │ │
│ │ 4. 다른 Replica들이 새 Master에 연결 │ │
│ │ 5. 클라이언트에게 새 Master 주소 전달 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 제약: │
│ • 수평 확장 불가 (단일 Master) │
│ • 쓰기는 Master에만 가능 │
│ │
└─────────────────────────────────────────────────────────────────┘
3.2 Redis Cluster¶
┌─────────────────────────────────────────────────────────────────┐
│ Redis Cluster │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "Redis 수평 확장 및 자동 샤딩" │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 16384 Hash Slots 분배: │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Node A │ │ Node B │ │ Node C │ │ │
│ │ │ Master │ │ Master │ │ Master │ │ │
│ │ │ Slots: │ │ Slots: │ │ Slots: │ │ │
│ │ │ 0-5460 │ │ 5461-10922 │ │ 10923-16383 │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │ │
│ │ ▼ ▼ ▼ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Replica A │ │ Replica B │ │ Replica C │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Hash Slot 계산: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ slot = CRC16(key) % 16384 │ │
│ │ │ │
│ │ 예: key = "user:123" │ │
│ │ CRC16("user:123") = 12345 │ │
│ │ slot = 12345 % 16384 = 12345 → Node C │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Hash Tag (같은 슬롯 강제): │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ # {user:123}이 해시 대상 │ │
│ │ user:{user:123}:profile │ │
│ │ user:{user:123}:settings │ │
│ │ user:{user:123}:orders │ │
│ │ │ │
│ │ → 모두 같은 슬롯에 저장됨 │ │
│ │ → 멀티 키 연산 가능 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 특징: │
│ • 수평 확장 가능 (노드 추가) │
│ • 자동 샤딩 │
│ • 고가용성 (Replica 자동 페일오버) │
│ • 일부 제약: 멀티 키 연산은 같은 슬롯만 가능 │
│ │
└─────────────────────────────────────────────────────────────────┘
3.3 Sentinel vs Cluster¶
| 항목 | Sentinel | Cluster |
|---|---|---|
| 목적 | 고가용성 | 고가용성 + 수평확장 |
| 샤딩 | 없음 (단일 Master) | 자동 샤딩 |
| 용량 | 단일 서버 메모리 | 분산 저장 |
| 복잡도 | 낮음 | 높음 |
| 멀티 키 연산 | 자유로움 | Hash Tag 필요 |
| 적합한 경우 | 중소규모 | 대규모 |
4. Memcached 비교¶
4.1 Redis vs Memcached¶
┌─────────────────────────────────────────────────────────────────┐
│ Redis vs Memcached │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Redis: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • 다양한 데이터 구조 (String, Hash, List, Set, ZSet) │ │
│ │ • 영속성 옵션 (RDB, AOF) │ │
│ │ • 복제 및 클러스터 지원 │ │
│ │ • Pub/Sub, Lua 스크립트 │ │
│ │ • 트랜잭션 (MULTI/EXEC) │ │
│ │ • 단일 스레드 (이벤트 루프) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Memcached: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ • 단순 키-값 저장 (String만) │ │
│ │ • 영속성 없음 (순수 캐시) │ │
│ │ • 멀티스레드 (멀티코어 활용) │ │
│ │ • 단순하고 가벼움 │ │
│ │ • LRU 캐시 정책 │ │
│ │ • 슬랩 할당자 (메모리 효율) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
4.2 비교 표¶
| 항목 | Redis | Memcached |
|---|---|---|
| 데이터 구조 | 다양함 | String만 |
| 영속성 | RDB, AOF | 없음 |
| 복제 | 지원 | 없음 |
| 클러스터 | 지원 | 클라이언트 샤딩 |
| 스레딩 | 싱글 (6.0+ 멀티) | 멀티 |
| 메모리 효율 | 좋음 | 매우 좋음 |
| 최대 값 크기 | 512MB | 1MB |
| 사용 사례 | 범용, 세션, 큐 | 단순 캐시 |
4.3 선택 기준¶
┌─────────────────────────────────────────────────────────────────┐
│ 선택 가이드 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Redis를 선택할 때: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ • 복잡한 데이터 구조 필요 (List, Set, Sorted Set) │ │
│ │ • 데이터 영속성 필요 │ │
│ │ • Pub/Sub, 메시지 큐 기능 필요 │ │
│ │ • 복제 및 고가용성 필요 │ │
│ │ • 세션 저장소 │ │
│ │ • 리더보드, Rate Limiting │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Memcached를 선택할 때: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ • 단순 키-값 캐시만 필요 │ │
│ │ • 멀티코어 활용 중요 │ │
│ │ • 메모리 효율이 중요 │ │
│ │ • 영속성 불필요 │ │
│ │ • 매우 높은 처리량 필요 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 대부분의 경우 Redis 권장 (기능 다양성, 생태계) │
│ │
└─────────────────────────────────────────────────────────────────┘
5. 일관성 해싱¶
5.1 기존 해싱의 문제¶
┌─────────────────────────────────────────────────────────────────┐
│ 기존 해싱 문제 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Modular 해싱: hash(key) % N │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 3개 서버: │ │
│ │ hash("user:1") % 3 = 1 → Server 1 │ │
│ │ hash("user:2") % 3 = 2 → Server 2 │ │
│ │ hash("user:3") % 3 = 0 → Server 0 │ │
│ │ │ │
│ │ 서버 1개 추가 (4개): │ │
│ │ hash("user:1") % 4 = 0 → Server 0 (변경!) │ │
│ │ hash("user:2") % 4 = 2 → Server 2 │ │
│ │ hash("user:3") % 4 = 3 → Server 3 (변경!) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 문제: 서버 추가/제거 시 대부분의 키가 재배치됨! │
│ → 캐시 미스 폭증 → 데이터베이스 부하 │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ N개 서버 → N+1개 서버: N/(N+1) 키가 재배치 │ │
│ │ 예: 100 → 101개 서버: 약 99%의 키 재배치! │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
5.2 일관성 해싱 원리¶
┌─────────────────────────────────────────────────────────────────┐
│ 일관성 해싱 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ "서버 추가/제거 시 최소한의 키만 재배치" │
│ │
│ 해시 링 (Hash Ring): │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 0 │ │
│ │ │ │ │
│ │ ┌────┴────┐ │ │
│ │ │ │ │ │
│ │ Node A ──● ●── key1 │ │
│ │ / \ │ │
│ │ / \ │ │
│ │ key2 ●───● ●─── Node B │ │
│ │ Node C \ │ │
│ │ \ \ │ │
│ │ \ ●── key3 │ │
│ │ \ / │ │
│ │ ●─────────────● │ │
│ │ │ │
│ │ 키 → 시계 방향으로 첫 번째 노드에 할당 │ │
│ │ │ │
│ │ key1 → Node B │ │
│ │ key2 → Node C │ │
│ │ key3 → Node A │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 노드 추가 시: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ Node A ──● │ │
│ │ / │ │
│ │ key2 ●───● ●── Node D (새로 추가) │ │
│ │ Node C │ │ │
│ │ ●── key1 (Node D로 이동)│ │
│ │ │ │ │
│ │ ●─────────────●──┘ │ │
│ │ Node B │ │
│ │ │ │
│ │ key1만 Node B → Node D로 이동 │ │
│ │ 나머지는 그대로! │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 이론: K/N 키만 재배치 (K=전체 키, N=노드 수) │
│ 예: 100개 노드 → 101개: 약 1%만 재배치! │
│ │
└─────────────────────────────────────────────────────────────────┘
5.3 가상 노드 (Virtual Nodes)¶
┌─────────────────────────────────────────────────────────────────┐
│ 가상 노드 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 문제: 노드가 적으면 불균등 분배 │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 2개 노드만 있을 때: │ │
│ │ │ │
│ │ Node A ──● │ │
│ │ / \ │ │
│ │ / \ │ │
│ │ / \ │ │
│ │ / 많은 키가 │ │
│ │ / Node B에 │ │
│ │ ●───── Node B │ │
│ │ │ │
│ │ → 불균등! │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 해결: 가상 노드 사용 │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 각 물리 노드를 여러 가상 노드로 표현 │ │
│ │ │ │
│ │ Node A → A-1, A-2, A-3, A-4, ... │ │
│ │ Node B → B-1, B-2, B-3, B-4, ... │ │
│ │ │ │
│ │ A-1 ● │ │
│ │ / B-2 ● │ │
│ │ / / A-3 ● │ │
│ │ B-1 ● / / │ │
│ │ / / │ │
│ │ A-2 ●─────●── B-3 │ │
│ │ │ │
│ │ → 더 균등한 분배! │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 가상 노드 수: │
│ • 많을수록 균등하지만 메모리 사용 증가 │
│ • 일반적으로 노드당 100-200개 │
│ │
└─────────────────────────────────────────────────────────────────┘
6. 연습 문제¶
문제 1: Redis 데이터 구조 선택¶
다음 요구사항에 적합한 Redis 데이터 구조를 선택하세요.
a) 사용자 세션 저장 (ID, 이름, 권한 등 포함) b) 실시간 게임 리더보드 c) 최근 본 상품 10개 저장 d) 사용자가 좋아요한 게시글 목록 e) 채팅방 메시지 큐
문제 2: Sentinel vs Cluster¶
다음 시나리오에 Sentinel과 Cluster 중 무엇이 적합한지 선택하세요.
a) 10GB 데이터, 고가용성 필요 b) 1TB 데이터, 수평 확장 필요 c) 복잡한 트랜잭션이 많은 서비스 d) 단순 캐시, 초당 100만 요청
문제 3: 일관성 해싱¶
4개의 캐시 서버가 있고, 1개를 추가할 때: a) 기존 해싱에서 몇 %의 키가 재배치되나요? b) 일관성 해싱에서 몇 %의 키가 재배치되나요?
문제 4: Redis 설계¶
소셜 미디어 팔로우 기능을 Redis로 설계하세요.
요구사항: - 사용자 A가 B를 팔로우 - A의 팔로잉 목록 조회 - B의 팔로워 목록 조회 - A와 B의 공통 팔로잉 찾기
정답¶
문제 1 정답¶
a) 사용자 세션: Hash
HSET session:abc123 user_id 123 name "John" role "admin"
b) 리더보드: Sorted Set
ZADD leaderboard 1000 "user:123" 950 "user:456"
c) 최근 본 상품: List (크기 제한)
LPUSH user:123:recent "product:789"
LTRIM user:123:recent 0 9 # 10개만 유지
d) 좋아요 게시글: Set
SADD user:123:likes "post:456" "post:789"
e) 채팅 메시지 큐: List
RPUSH chat:room1 "{message...}"
BLPOP chat:room1 0 # 소비자가 대기
문제 2 정답¶
a) 10GB, 고가용성: Sentinel
- 데이터 크기가 단일 서버에 적합
- Sentinel로 고가용성 확보
- 구성이 간단
b) 1TB, 수평 확장: Cluster
- 대용량으로 분산 저장 필요
- 수평 확장으로 처리량 증가
c) 복잡한 트랜잭션: Sentinel
- Cluster는 멀티 키 트랜잭션 제약
- 단일 Master로 트랜잭션 용이
d) 단순 캐시, 고처리량: Cluster 또는 Memcached
- Cluster로 부하 분산
- 단순 캐시면 Memcached도 고려
문제 3 정답¶
a) 기존 해싱: 약 80%
N/(N+1) = 4/5 = 80%의 키 재배치
b) 일관성 해싱: 약 20%
K/N = 1/5 = 20%의 키만 재배치
(새 노드가 담당할 범위의 키만)
문제 4 정답¶
# 팔로우 설정
# A가 B를 팔로우
SADD user:A:following "B"
SADD user:B:followers "A"
# A의 팔로잉 목록
SMEMBERS user:A:following
# B의 팔로워 목록
SMEMBERS user:B:followers
# A와 B의 공통 팔로잉
SINTER user:A:following user:B:following
# 팔로워 수
SCARD user:B:followers
# 팔로우 여부 확인
SISMEMBER user:A:following "B"
# 언팔로우
SREM user:A:following "B"
SREM user:B:followers "A"
7. 다음 단계¶
분산 캐시를 이해했다면, 데이터베이스 확장을 학습하세요.
다음 레슨¶
관련 레슨¶
- 06_Caching_Strategies.md - 캐싱 패턴
- 09_Database_Replication.md - 복제 전략
추천 실습¶
- Redis 설치 및 데이터 구조 실습
- Redis Sentinel 구성해보기
- 일관성 해싱 직접 구현해보기
8. 참고 자료¶
공식 문서¶
도구¶
- Redis Commander - GUI
- RedisInsight - 공식 GUI
논문¶
- "Consistent Hashing and Random Trees" - Karger et al.
문서 정보 - 최종 수정: 2024년 - 난이도: ⭐⭐⭐ - 예상 학습 시간: 2-3시간