분산 캐시 시스템

분산 캐시 시스템

개요

이 문서에서는 분산 캐시 시스템의 핵심 개념을 다룹니다. Redis의 데이터 구조와 클러스터 구성, Memcached와의 비교, 그리고 일관성 해싱(Consistent Hashing)을 학습합니다.

난이도: ⭐⭐⭐ 예상 학습 시간: 2-3시간 선수 지식: 06_Caching_Strategies.md


목차

  1. 분산 캐시란?
  2. Redis 데이터 구조
  3. Redis 클러스터와 Sentinel
  4. Memcached 비교
  5. 일관성 해싱
  6. 연습 문제
  7. 다음 단계
  8. 참고 자료

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. 다음 단계

분산 캐시를 이해했다면, 데이터베이스 확장을 학습하세요.

다음 레슨

관련 레슨

추천 실습

  1. Redis 설치 및 데이터 구조 실습
  2. Redis Sentinel 구성해보기
  3. 일관성 해싱 직접 구현해보기

8. 참고 자료

공식 문서

도구

논문

  • "Consistent Hashing and Random Trees" - Karger et al.

문서 정보 - 최종 수정: 2024년 - 난이도: ⭐⭐⭐ - 예상 학습 시간: 2-3시간

to navigate between lessons