Redis 내부 원리 & ElastiCache 운영
분류: Layer 8 - DB 심화 | 작성일: 2026-04-10
📌 Redis를 복원력 패턴(Cache Stampede, Circuit Breaker 등)으로 활용하는 방법은 L6/redis-cache-basics.md에서 다룹니다. 이 문서는 Redis의 내부 동작 원리와 ElastiCache 운영 을 다룹니다.
1. 한 줄 정의
섹션 제목: “1. 한 줄 정의”Redis는 단일 스레드 이벤트 루프 위에서 다양한 자료구조를 메모리에 저장하고, RDB/AOF 영속화와 클러스터 샤딩을 통해 내구성과 수평 확장을 제공하는 In-Memory 데이터 스토어다.
2. 왜 L8 DB 심화 토픽인가
섹션 제목: “2. 왜 L8 DB 심화 토픽인가”L8 테마는 “데이터 저장소의 내부를 이해하고 운영 튜닝을 수행하기”다. Redis를 단순히 SET/GET으로 사용하는 것과, 내부 자료구조·영속화·클러스터 원리를 이해하고 운영하는 것은 다음에서 차이가 난다:
| 상황 | 내부 원리 이해 없이 | 내부 원리 이해 후 |
|---|---|---|
| Sorted Set 100만 건 ZRANGE 요청 | 느린 이유를 모름 | Skip List O(log N) 특성 이해 → 적절한 범위 쿼리 |
| Redis 재시작 후 데이터 손실 | 원인 파악 불가 | RDB vs AOF 영속화 설정 문제로 즉시 진단 |
| ElastiCache 클러스터 모드 CROSSSLOT 에러 | 개별 GET으로 바꾸면 해결 정도만 앎 | CRC16 Hash Slot 원리 이해 → Hash Tag로 최적화 |
| 메모리 사용량 급증 | 모니터링만 확인 | ziplist→hashtable 변환 임계값 튜닝으로 예방 |
3. 핵심 개념
섹션 제목: “3. 핵심 개념”3-1. Redis 내부 자료구조 원리
섹션 제목: “3-1. Redis 내부 자료구조 원리”Redis는 자료구조별로 데이터 크기에 따라 **압축 인코딩(compact encoding)**을 자동 선택한다. 소규모 데이터에서는 메모리를 아끼는 압축 형식을 쓰고, 임계값을 넘으면 연산 효율 우선 형식으로 변환한다.
String — SDS (Simple Dynamic String)
섹션 제목: “String — SDS (Simple Dynamic String)”Redis의 기본 문자열 타입. C의 char*를 쓰지 않고 자체 SDS 구조체를 사용한다.
/* SDS 구조체 (단순화) */struct sdshdr { int len; // 현재 문자열 길이 int free; // 남은 여유 공간 char buf[]; // 실제 데이터};SDS 장점:
O(1)길이 조회 (len 필드 저장)- 이진 안전(binary-safe) — null 문자 포함 가능
- 사전 할당(pre-allocation)으로 append 성능 향상
인코딩 선택:
| 조건 | 인코딩 | 설명 |
|---|---|---|
정수 범위 내 값 (0~9999999999) | int | 포인터 자체에 정수 저장 (8 bytes) |
| 44 bytes 이하 문자열 | embstr | 헤더+데이터를 단일 메모리 블록 |
| 44 bytes 초과 | raw | 일반 SDS |
# 인코딩 확인SET num 42OBJECT ENCODING num # → int
SET short "hello"OBJECT ENCODING short # → embstr
SET long "this is a very long string that exceeds 44 bytes limit"OBJECT ENCODING long # → rawHash — ziplist → hashtable
섹션 제목: “Hash — ziplist → hashtable”ziplist (압축 리스트): 항목 수가 적고(hash-max-ziplist-entries: 128) 값이 작을 때(hash-max-ziplist-value: 64 bytes) 사용. 연속된 메모리 블록에 저장하여 캐시 효율 극대화.
[zlbytes][zltail][zllen][entry1][entry2]...[entryN][zlend]hashtable: 임계값 초과 시 자동 전환. 키-값 쌍을 해시 테이블로 저장.
# 임계값 확인/변경CONFIG GET hash-max-ziplist-entries # → 128CONFIG SET hash-max-ziplist-entries 64
# 인코딩 확인HSET user:1 name "Alice" age 30OBJECT ENCODING user:1 # → ziplist (항목 수 적음)실무 튜닝 포인트: 작은 해시 수백만 개를 저장할 때 ziplist 임계값을 늘리면 메모리를 30~50% 절약할 수 있다. 단, 조회 시간은 O(N)으로 증가하므로 항목 수와 트레이드오프를 고려한다.
List — listpack → quicklist
섹션 제목: “List — listpack → quicklist”listpack (Redis 7.0+, 구버전은 ziplist): 소규모 리스트에서 사용하는 압축 인코딩.
quicklist: 임계값(list-max-ziplist-size: 128) 초과 시 전환. 여러 개의 listpack을 이중 연결 리스트로 연결한 구조.
quicklist:node1(listpack) ↔ node2(listpack) ↔ node3(listpack)LPUSH/RPUSH: O(1)LINDEX: O(N) — 중간 접근 비용 주의LRANGE 0 -1: O(N) — 전체 조회 비용 주의
Set — listpack → hashtable / intset
섹션 제목: “Set — listpack → hashtable / intset”intset: 모든 요소가 정수이고 64개 이하일 때 사용. 정렬된 정수 배열로 이진 탐색.
listpack: 소규모 문자열 셋에서 사용 (Redis 7.2+).
hashtable: 요소가 많거나 문자열이 길면 전환.
SADD intset-example 1 2 3 4 5OBJECT ENCODING intset-example # → intset
SADD str-example "apple" "banana"OBJECT ENCODING str-example # → listpack 또는 hashtableSorted Set — listpack → skiplist + hashtable
섹션 제목: “Sorted Set — listpack → skiplist + hashtable”가장 복잡한 자료구조. listpack 임계값(zset-max-listpack-entries: 128, zset-max-listpack-value: 64) 초과 시 Skip List + Hash Table 조합으로 전환.
Skip List 구조:
Level 4: [head] ────────────────────────── [tail]Level 3: [head] ──────── [30] ───────────── [tail]Level 2: [head] ── [10] ─ [30] ── [50] ──── [tail]Level 1: [head] ─ [10] ─ [20] ─ [30] ─ [40] ─ [50] ─ [tail]- 삽입/삭제: O(log N) 평균
- 범위 조회(
ZRANGE,ZRANGEBYSCORE): O(log N + M) - 특정 Score 조회: O(log N)
Hash Table (병용): Score로 정렬된 Skip List와 별도로, 멤버 → Score 매핑을 위한 해시 테이블도 유지. ZSCORE 커맨드가 O(1)인 이유다.
# ZADD: Skip List에 삽입 (O(log N))ZADD leaderboard 1500 "alice" 2000 "bob" 1200 "charlie"
# ZRANGE: 순위 범위 조회 (O(log N + M))ZRANGE leaderboard 0 -1 WITHSCORES
# ZSCORE: Hash Table 조회 (O(1))ZSCORE leaderboard "alice"
# ZRANK: Skip List 순회 (O(log N))ZRANK leaderboard "alice"3-2. 단일 스레드 + I/O Multiplexing 원리
섹션 제목: “3-2. 단일 스레드 + I/O Multiplexing 원리”Redis는 단일 스레드로 모든 커맨드를 처리한다. 이것이 어떻게 초당 수십만 건의 처리를 가능하게 하는가?
이벤트 루프 구조
섹션 제목: “이벤트 루프 구조”클라이언트 연결들 ↓[epoll/kqueue] ← I/O Multiplexing (OS 레벨) ↓이벤트 루프 (단일 스레드) ↓커맨드 큐 → 순차 실행 → 응답 반환핵심 원리:
- 비블로킹 I/O:
epoll(Linux) /kqueue(macOS)로 수천 개의 소켓을 동시에 감시. 데이터가 준비된 소켓만 처리. - 메모리 연산: 디스크 I/O 없이 RAM에서 직접 연산 → 마이크로초 단위 응답.
- 단일 스레드의 장점: 락(Lock) 없음 → 컨텍스트 스위칭 오버헤드 없음 → 모든 커맨드가 원자적(Atomic).
싱글 스레드라 Lock 없음→ INCR이 항상 원자적 (Read-Modify-Write가 끊기지 않음)→ MULTI/EXEC 트랜잭션이 단순하게 동작단일 스레드의 약점과 대응
섹션 제목: “단일 스레드의 약점과 대응”| 약점 | 대응 방법 |
|---|---|
| CPU 코어 하나만 사용 | 여러 Redis 인스턴스 실행 (포트별) |
| 블로킹 커맨드(KEYS *, SORT 대용량) | SCAN으로 대체, 대용량 연산 분리 |
| 대용량 데이터 직렬화 비용 | Pipeline으로 네트워크 왕복 감소 |
Redis 6.0+ I/O 스레드: 네트워크 읽기/쓰기는 멀티스레드로 처리하되, 커맨드 실행 자체는 여전히 단일 스레드. 처리량이 20~30% 향상.
# redis.confio-threads 4 # I/O 스레드 수 (CPU 코어 수보다 작게)io-threads-do-reads yes3-3. RDB vs AOF 영속화 비교
섹션 제목: “3-3. RDB vs AOF 영속화 비교”Redis는 기본적으로 메모리 저장소다. 재시작 시 데이터를 복구하려면 영속화(Persistence)가 필요하다.
RDB (Redis Database) — 스냅샷 방식
섹션 제목: “RDB (Redis Database) — 스냅샷 방식”특정 시점의 전체 데이터를 바이너리 파일(.rdb)로 덤프.
# redis.confsave 900 1 # 900초(15분) 내에 1번 이상 변경 시 저장save 300 10 # 300초(5분) 내에 10번 이상 변경 시 저장save 60 10000 # 60초 내에 10,000번 이상 변경 시 저장
dbfilename dump.rdbdir /var/lib/redisRDB 저장 메커니즘 — fork + Copy-On-Write:
메인 프로세스 (서비스 계속) ↓ fork()자식 프로세스 → RDB 파일 작성 (부모 메모리를 복사하지 않고 참조) ↓ Copy-On-Write부모가 데이터 수정 시에만 해당 페이지 복사fork()시점에 메모리 전체를 복사하지 않음 → 메모리 효율적- 자식이 쓰는 동안 부모는 계속 요청 처리
fork()자체는 짧은 블로킹 발생 (데이터 크기에 비례)
RDB 장점/단점:
| 항목 | 내용 |
|---|---|
| 장점 | 파일 크기 작음, 복구 빠름, 성능 영향 적음 |
| 단점 | 마지막 스냅샷 이후 데이터 손실 가능 (최대 수분) |
| 적합 | 주기적 백업, 데이터 손실 허용 가능한 캐시 용도 |
AOF (Append Only File) — 로그 방식
섹션 제목: “AOF (Append Only File) — 로그 방식”모든 쓰기 커맨드를 순서대로 파일에 기록. 재시작 시 파일을 replay하여 복구.
# redis.confappendonly yesappendfilename "appendonly.aof"
# fsync 정책 (핵심 선택)appendfsync always # 매 쓰기마다 fsync → 가장 안전, 가장 느림appendfsync everysec # 1초마다 fsync → 최대 1초치 손실, 권장값appendfsync no # OS에 위임 → 빠르지만 위험AOF Rewrite (압축):
AOF 파일이 커지면 재작성:SET a 1 → SET a 2 → SET a 3 → ... (수천 줄) ↓ BGREWRITEAOFSET a 3 (현재 상태만 기록, 중간 과정 제거)# 자동 Rewrite 설정auto-aof-rewrite-percentage 100 # 파일 크기가 2배 될 때auto-aof-rewrite-min-size 64mb # 최소 64MB 이상일 때RDB + AOF 혼합 모드 (Redis 4.0+, 권장)
섹션 제목: “RDB + AOF 혼합 모드 (Redis 4.0+, 권장)”# redis.confaof-use-rdb-preamble yes # AOF 파일의 앞부분은 RDB 형식으로 저장- 재시작 시 RDB로 빠르게 로드 → 이후 AOF 로그만 replay → 빠른 복구 + 낮은 데이터 손실
- BullMQ 등 작업 큐를 Redis에 저장할 때 반드시 AOF 활성화 필요
영속화 전략 선택 가이드
섹션 제목: “영속화 전략 선택 가이드”| 상황 | 권장 설정 |
|---|---|
| 순수 캐시 (손실 허용) | RDB만 (AOF 비활성화) |
| 세션 저장 (최대 1초 손실 허용) | AOF everysec |
| BullMQ 큐 (손실 불허) | AOF always 또는 혼합 |
| 실시간 랭킹 (재연산 가능) | RDB만 |
3-4. Redis Cluster — Hash Slot & CRC16
섹션 제목: “3-4. Redis Cluster — Hash Slot & CRC16”단일 Redis 인스턴스의 메모리 한계를 넘어 수평 확장하는 방법.
Hash Slot 분배 원리
섹션 제목: “Hash Slot 분배 원리”총 16,384개의 슬롯 → N개의 마스터 노드에 균등 분배
키 → CRC16(key) % 16384 = 슬롯 번호 → 해당 슬롯을 가진 노드로 라우팅
예시 (3 마스터):노드 A: 슬롯 0 ~ 5460노드 B: 슬롯 5461 ~ 10922노드 C: 슬롯 10923 ~ 16383# 슬롯 확인redis-cli --cluster check [primary-endpoint]:6379
# 특정 키가 몇 번 슬롯인지 확인redis-cli CLUSTER KEYSLOT "product:123" # → e.g., 7638 (노드 B 담당)Hash Tag — 동일 슬롯 강제 지정
섹션 제목: “Hash Tag — 동일 슬롯 강제 지정”일반 키: CRC16("user:123") % 16384 → 임의 슬롯Hash Tag: CRC16("{user}") % 16384 → {user}로 묶인 키는 모두 같은 슬롯
{user}:123, {user}:456, {user}:789 → 모두 동일 슬롯 → MGET/Pipeline 가능// Hash Tag 활용const keys = ["{user}:123", "{user}:456", "{user}:789"];const values = await redis.mget(...keys); // CROSSSLOT 에러 없음MOVED & ASK 리다이렉션
섹션 제목: “MOVED & ASK 리다이렉션”클라이언트 → 노드 A에 요청 (키가 노드 B에 있음)노드 A → MOVED 7638 [노드B IP]:6379클라이언트 → 노드 B로 재요청클라이언트 → 슬롯 맵 캐시 업데이트 (다음 요청부터 직접 접근)ioredis는 자동으로 클러스터 슬롯 맵을 캐시하고 MOVED/ASK를 처리한다.
import { Cluster } from "ioredis";
const cluster = new Cluster( [ { host: "node-a", port: 6379 }, { host: "node-b", port: 6379 }, { host: "node-c", port: 6379 }, ], { redisOptions: { password: process.env.REDIS_PASSWORD, }, // 슬롯 맵 갱신 간격 slotsRefreshTimeout: 2000, slotsRefreshInterval: 10_000, },);클러스터 복제 구조
섹션 제목: “클러스터 복제 구조”마스터 A (슬롯 0~5460) ← 복제 → 슬레이브 A'마스터 B (슬롯 5461~10922) ← 복제 → 슬레이브 B'마스터 C (슬롯 10923~16383) ← 복제 → 슬레이브 C'- 마스터 장애 시 슬레이브가 자동으로 마스터로 승격 (Failover)
- 과반수 마스터가 살아있어야 클러스터 정상 동작 (최소 3 마스터 권장)
3-5. Sentinel — 고가용성 (HA)
섹션 제목: “3-5. Sentinel — 고가용성 (HA)”클러스터 모드 없이 단일 Primary + Replica 구성에서 자동 Failover를 제공한다.
Sentinel 구조
섹션 제목: “Sentinel 구조”Sentinel 1 Sentinel 2 Sentinel 3 ↓ ↓ ↓ Primary Redis ↓ Replica RedisSentinel 역할:
- Monitoring: Primary/Replica 상태를 주기적으로 확인
- Notification: 장애 감지 시 알림
- Automatic Failover: Primary 장애 시 Replica를 자동 승격
- Configuration Provider: 클라이언트가 현재 Primary 주소를 Sentinel에 질의
# sentinel.confsentinel monitor mymaster [primary-ip] 6379 2 # 과반수(2개) 동의 필요sentinel down-after-milliseconds mymaster 5000 # 5초 무응답 → 장애 판정sentinel failover-timeout mymaster 60000 # 60초 Failover 타임아웃// ioredis Sentinel 연결const redis = new Redis({ sentinels: [ { host: "sentinel-1", port: 26379 }, { host: "sentinel-2", port: 26379 }, { host: "sentinel-3", port: 26379 }, ], name: "mymaster", // Sentinel이 모니터링하는 이름 password: process.env.REDIS_PASSWORD,});Sentinel vs Cluster 선택 기준
섹션 제목: “Sentinel vs Cluster 선택 기준”| 항목 | Sentinel | Cluster |
|---|---|---|
| 사용 목적 | HA (고가용성) | HA + 수평 확장 |
| 데이터 분산 | 없음 (모든 데이터가 Primary에) | 있음 (16,384 슬롯으로 분산) |
| 메모리 한계 | 단일 노드 메모리 | 노드 수 × 단일 노드 메모리 |
| 구성 복잡도 | 낮음 | 높음 |
| 다중 키 연산 | 제한 없음 | 동일 슬롯 키만 (CROSSSLOT 주의) |
| 적합한 경우 | 수십~수백 GB 이하, 단순 구성 | 수 TB, 초고처리량 |
3-6. ElastiCache 운영 튜닝
섹션 제목: “3-6. ElastiCache 운영 튜닝”AWS ElastiCache는 Redis를 관리형으로 제공한다. 핵심 튜닝 포인트를 정리한다.
파라미터 그룹 주요 설정
섹션 제목: “파라미터 그룹 주요 설정”# ElastiCache 파라미터 그룹에서 변경 가능한 핵심 값들
# 메모리 관리maxmemory-policy = allkeys-lru # 캐시 용도의 경우 권장reserved-memory-percent = 25 # 복제/AOF용 메모리 예약 (%)
# 영속화appendonly = yes # AOF 활성화 (BullMQ 필수)appendfsync = everysec # 1초마다 flush
# 연결 관리maxclients = 65000 # 최대 동시 연결 수tcp-keepalive = 300 # 유휴 연결 유지 간격 (초)timeout = 0 # 클라이언트 타임아웃 (0=비활성)
# 자료구조 압축 임계값 (메모리 최적화)hash-max-listpack-entries = 128hash-max-listpack-value = 64zset-max-listpack-entries = 128zset-max-listpack-value = 64노드 타입 선택 가이드
섹션 제목: “노드 타입 선택 가이드”| 워크로드 | 권장 노드 타입 | 이유 |
|---|---|---|
| 개발/소규모 | cache.t3.micro | 최소 비용 |
| 일반 캐시 (수십 GB) | cache.r6g.large | 메모리 최적화, Graviton2 |
| 고처리량 (초당 수십만 요청) | cache.r6g.xlarge 이상 | 네트워크 대역폭 충분 |
| BullMQ/세션 (영속화 필수) | cache.r6g.* + AOF | 메모리 여유 필요 (AOF 오버헤드) |
CloudWatch 핵심 지표
섹션 제목: “CloudWatch 핵심 지표”| 지표 | 정상 범위 | 경보 기준 |
|---|---|---|
EngineCPUUtilization | < 50% | > 80% 지속 시 알람 |
DatabaseMemoryUsagePercentage | < 75% | > 80% 시 즉시 대응 |
CacheHitRate | > 90% | < 80% 지속 시 캐시 전략 점검 |
CurrConnections | 안정적 | 급증 시 연결 누수 점검 |
Evictions | 0 (캐시 외 용도) | 증가 시 메모리 부족 신호 |
ReplicationLag | < 1초 | 지속 증가 시 복제 지연 점검 |
# CloudWatch Alarm 생성 예시 (AWS CLI)aws cloudwatch put-metric-alarm \ --alarm-name "redis-memory-high" \ --metric-name DatabaseMemoryUsagePercentage \ --namespace AWS/ElastiCache \ --statistic Average \ --period 300 \ --threshold 80 \ --comparison-operator GreaterThanThreshold \ --evaluation-periods 2 \ --alarm-actions [SNS-ARN]ElastiCache 클러스터 모드 vs 비클러스터 모드
섹션 제목: “ElastiCache 클러스터 모드 vs 비클러스터 모드”비클러스터 모드 (Cluster Mode Disabled): Primary + Replica(s) → Sentinel과 유사, 단일 엔드포인트 제공 → 읽기: Reader Endpoint, 쓰기: Primary Endpoint
클러스터 모드 (Cluster Mode Enabled): 여러 샤드 × (Primary + Replica) → Configuration Endpoint 하나로 접속 → ioredis의 Cluster 클라이언트 필요// 비클러스터 모드 (ioredis)const redis = new Redis({ host: process.env.REDIS_PRIMARY_HOST, // Primary Endpoint port: 6379, tls: {}, password: process.env.REDIS_AUTH_TOKEN,});
// 읽기 분산 (Reader Endpoint)const redisReader = new Redis({ host: process.env.REDIS_READER_HOST, // Reader Endpoint port: 6379, tls: {}, password: process.env.REDIS_AUTH_TOKEN,});
// 클러스터 모드 (ioredis)const cluster = new Cluster( [{ host: process.env.ELASTICACHE_CONFIG_ENDPOINT, port: 6379 }], { redisOptions: { tls: {}, password: process.env.REDIS_AUTH_TOKEN, }, clusterRetryStrategy: (times) => Math.min(times * 200, 2000), enableOfflineQueue: false, // 연결 끊겼을 때 커맨드 큐잉 비활성화 (Failfast) },);4. 실무에서 어떻게 쓰이나
섹션 제목: “4. 실무에서 어떻게 쓰이나”BackOps 환경에서 내부 원리가 필요한 순간
섹션 제목: “BackOps 환경에서 내부 원리가 필요한 순간”| 상황 | 관련 원리 |
|---|---|
| BullMQ 큐 데이터가 재시작 후 사라짐 | AOF 영속화 미설정 → 3-3 참고 |
| ElastiCache CROSSSLOT 에러 | Hash Slot 원리 → 3-4 참고 |
| Sorted Set 조회가 느려짐 | Skip List 특성 → 3-1 참고 |
| 메모리 사용량이 예상보다 높음 | ziplist→hashtable 변환 임계값 → 3-1 참고 |
| ElastiCache 노드 장애 시 Failover 속도 | Sentinel vs Cluster 구조 → 3-5 참고 |
| Redis 재시작 후 복구 시간이 너무 긴 경우 | RDB + AOF 혼합 모드 → 3-3 참고 |
5. 비교 / 대안
섹션 제목: “5. 비교 / 대안”Redis vs Memcached — 자세한 비교
섹션 제목: “Redis vs Memcached — 자세한 비교”| 항목 | Redis | Memcached |
|---|---|---|
| 자료구조 | String, Hash, List, Set, ZSet 등 | 단순 String만 |
| 영속화 | RDB + AOF 지원 | 없음 (재시작 시 전체 손실) |
| 클러스터링 | 내장 Cluster 모드 | 클라이언트 사이드 샤딩만 |
| 멀티스레드 | 싱글 스레드 (I/O는 멀티) | 완전 멀티스레드 |
| Pub/Sub | 지원 | 미지원 |
| Lua 스크립트 | 지원 | 미지원 |
| 메모리 효율 | 자료구조별 오버헤드 있음 | 순수 Key-Value로 효율적 |
| 적합한 경우 | 캐시 + 큐 + 세션 + 랭킹 | 초대규모 단순 캐시 (멀티코어 활용) |
6. 체크리스트
섹션 제목: “6. 체크리스트”내부 원리 이해
섹션 제목: “내부 원리 이해”- 각 자료구조의 인코딩 전환 임계값을 알고 있는가? (
OBJECT ENCODING확인) - 단일 스레드 특성상 블로킹 커맨드(
KEYS *, 대용량SMEMBERS)를 피하는가? -
SCAN을 사용하여 대용량 키 순회를 비블로킹으로 처리하는가?
영속화 설정
섹션 제목: “영속화 설정”- BullMQ 사용 시 AOF가 활성화되어 있는가?
-
appendfsync everysec이상으로 설정되어 있는가? - 혼합 모드(
aof-use-rdb-preamble yes)를 사용하여 복구 속도를 최적화했는가?
클러스터/Sentinel 운영
섹션 제목: “클러스터/Sentinel 운영”- 멀티 키 커맨드(
MGET, Pipeline)에서 CROSSSLOT 에러를 처리했는가? - Hash Tag를 사용하여 관련 키를 동일 슬롯에 배치했는가?
- Sentinel 또는 Cluster 구성에서 Failover 시나리오를 테스트했는가?
ElastiCache 튜닝
섹션 제목: “ElastiCache 튜닝”-
maxmemory-policy를 용도에 맞게 설정했는가? -
reserved-memory-percent를 AOF + 복제 오버헤드에 맞게 설정했는가? - CloudWatch 주요 지표 알람을 설정했는가?
- 읽기 부하 분산을 위해 Reader Endpoint를 활용하는가?
7. 키워드
섹션 제목: “7. 키워드”| 키워드 | 설명 |
|---|---|
| SDS | Simple Dynamic String — Redis의 문자열 내부 구현체 |
| ziplist / listpack | 소규모 자료구조를 위한 압축 인코딩 형식 |
| Skip List | Sorted Set의 범위 쿼리를 O(log N)으로 처리하는 자료구조 |
| I/O Multiplexing | epoll/kqueue로 다수 소켓을 단일 스레드에서 처리하는 기법 |
| RDB | Redis Database — 특정 시점의 스냅샷 파일 |
| AOF | Append Only File — 모든 쓰기 커맨드를 순서대로 기록하는 영속화 |
| BGSAVE | 백그라운드 RDB 저장 (fork + Copy-On-Write) |
| BGREWRITEAOF | 백그라운드 AOF 재작성 (파일 크기 압축) |
| Hash Slot | Redis Cluster의 데이터 분산 단위 (총 16,384개) |
| CRC16 | 키를 슬롯 번호로 매핑하는 해시 함수 |
| Hash Tag | {tag}:key 형식으로 동일 슬롯 강제 지정 |
| MOVED | 클라이언트가 다른 노드로 재요청해야 할 때 반환되는 에러 |
| ASK | 슬롯 이동 중(Resharding) 임시 리다이렉션 에러 |
| Sentinel | Redis Primary 장애 시 자동 Failover를 수행하는 고가용성 컴포넌트 |
| Configuration Endpoint | ElastiCache 클러스터 모드의 단일 진입점 |
| Reader Endpoint | ElastiCache 비클러스터 모드의 읽기 전용 엔드포인트 |
| reserved-memory-percent | AOF/복제를 위해 예약하는 메모리 비율 |
| EngineCPUUtilization | Redis 프로세스의 CPU 사용률 (CloudWatch 지표) |
| CacheHitRate | 캐시 적중률 (CloudWatch 지표) |
8. 추천 리소스
섹션 제목: “8. 추천 리소스”- Redis Internals — How Redis Works — Redis 공식 문서, 자료구조 내부 구현 설명 (심화)
- Redis Persistence Demystified — Redis 창시자 antirez가 직접 쓴 RDB/AOF 설명 (중급)
- Understanding Redis Cluster — Redis Cluster 공식 가이드 (중급~심화)
- AWS ElastiCache Best Practices — ElastiCache 운영 공식 가이드 (중급)
- Redis University — Redis for Developers — Redis 공식 무료 온라인 강의 (입문~심화)
9. 직접 확인해보기
섹션 제목: “9. 직접 확인해보기”실습 1: 자료구조 인코딩 관찰
섹션 제목: “실습 1: 자료구조 인코딩 관찰”docker run -d --name redis-intern -p 6379:6379 redis:7-alpinedocker exec -it redis-intern redis-cli
# String 인코딩SET num 42OBJECT ENCODING num # → intSET short "hello"OBJECT ENCODING short # → embstrSET long "$(python3 -c "print('a'*50)")"OBJECT ENCODING long # → raw
# Hash 인코딩 (ziplist → hashtable 전환 관찰)HSET small-hash a 1 b 2OBJECT ENCODING small-hash # → listpack (또는 ziplist)
# 128개 이상 필드 추가for i in $(seq 1 130); do redis-cli HSET big-hash field$i value$i; doneOBJECT ENCODING big-hash # → hashtable
# Sorted Set 인코딩ZADD small-zset 1 "a" 2 "b"OBJECT ENCODING small-zset # → listpack실습 2: RDB / AOF 동작 확인
섹션 제목: “실습 2: RDB / AOF 동작 확인”# AOF 활성화 후 커맨드 기록 확인docker run -d --name redis-aof -p 6380:6379 redis:7-alpine \ redis-server --appendonly yes
docker exec -it redis-aof redis-cli -p 6379SET test-key "hello"SET another-key "world"EXIT
# AOF 파일 내용 확인docker exec redis-aof cat /data/appendonly.aof# → *2, $6, SELECT, $1, 0 등 RESP 형식으로 기록된 커맨드 확인
# 수동 RDB 저장docker exec -it redis-aof redis-cli BGSAVEdocker exec redis-aof ls -la /data/ # dump.rdb 파일 생성 확인실습 3: Skip List 성능 확인
섹션 제목: “실습 3: Skip List 성능 확인”# 100만 건 Sorted Set 생성 (성능 테스트)redis-cli -n 0 EVAL " for i=1,1000000 do redis.call('zadd', 'bigzset', i, 'member:'..i) end" 0
# 범위 쿼리 성능 측정redis-cli --latency-history -i 1 -p 6379
# 순위 범위 조회 (O(log N + M))time redis-cli ZRANGE bigzset 0 99 WITHSCORES
# 점수 범위 조회time redis-cli ZRANGEBYSCORE bigzset 500000 500100 WITHSCORES실습 4: ElastiCache Hash Slot 확인
섹션 제목: “실습 4: ElastiCache Hash Slot 확인”# 로컬 클러스터 시뮬레이션docker network create redis-cluster-net
# 6개 노드 실행 (3 master + 3 replica)for port in 7001 7002 7003 7004 7005 7006; do docker run -d --name redis-$port --net redis-cluster-net \ -p $port:6379 redis:7-alpine \ redis-server --cluster-enabled yes --cluster-config-file nodes.conf \ --cluster-node-timeout 5000 --appendonly yesdone
# 클러스터 생성docker exec redis-7001 redis-cli --cluster create \ redis-7001:6379 redis-7002:6379 redis-7003:6379 \ redis-7004:6379 redis-7005:6379 redis-7006:6379 \ --cluster-replicas 1 --cluster-yes
# 슬롯 확인docker exec -it redis-7001 redis-cli CLUSTER INFOdocker exec -it redis-7001 redis-cli CLUSTER NODES
# Hash Tag 실습docker exec -it redis-7001 redis-cli -cCLUSTER KEYSLOT "{user}:123"CLUSTER KEYSLOT "{user}:456"# 같은 슬롯 번호인지 확인MSET "{user}:123" "alice" "{user}:456" "bob" # CROSSSLOT 없음10. 요약
섹션 제목: “10. 요약”Redis 내부 원리를 이해하면 세 가지 영역에서 실질적인 차이를 만들 수 있다.
자료구조 최적화:
- 각 자료구조의 압축 인코딩 임계값을 조정하면 메모리를 30~50% 절약 가능
- Sorted Set의 Skip List 특성을 이해하면 올바른 범위 쿼리를 설계할 수 있음
영속화 전략:
- BullMQ 등 손실이 허용되지 않는 데이터:
AOF always또는 혼합 모드 - 순수 캐시: RDB만으로 충분
aof-use-rdb-preamble yes로 빠른 복구 + 낮은 손실을 동시에 달성
클러스터 운영:
- Hash Slot 원리를 이해하면 CROSSSLOT 에러를 Hash Tag로 예방
- Sentinel vs Cluster 구분 기준: 수평 확장 필요 여부
- ElastiCache 파라미터 그룹 튜닝으로 메모리 효율과 성능을 최적화
복원력 패턴(Cache Stampede, Circuit Breaker, Rate Limiting)은
L6/redis-cache-basics.md를 참고한다.