콘텐츠로 이동

Redis 내부 원리 & ElastiCache 운영

분류: Layer 8 - DB 심화 | 작성일: 2026-04-10

📌 Redis를 복원력 패턴(Cache Stampede, Circuit Breaker 등)으로 활용하는 방법은 L6/redis-cache-basics.md에서 다룹니다. 이 문서는 Redis의 내부 동작 원리ElastiCache 운영 을 다룹니다.


Redis는 단일 스레드 이벤트 루프 위에서 다양한 자료구조를 메모리에 저장하고, RDB/AOF 영속화와 클러스터 샤딩을 통해 내구성과 수평 확장을 제공하는 In-Memory 데이터 스토어다.


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 변환 임계값 튜닝으로 예방

Redis는 자료구조별로 데이터 크기에 따라 **압축 인코딩(compact encoding)**을 자동 선택한다. 소규모 데이터에서는 메모리를 아끼는 압축 형식을 쓰고, 임계값을 넘으면 연산 효율 우선 형식으로 변환한다.

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
Terminal window
# 인코딩 확인
SET num 42
OBJECT 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 # → raw

ziplist (압축 리스트): 항목 수가 적고(hash-max-ziplist-entries: 128) 값이 작을 때(hash-max-ziplist-value: 64 bytes) 사용. 연속된 메모리 블록에 저장하여 캐시 효율 극대화.

[zlbytes][zltail][zllen][entry1][entry2]...[entryN][zlend]

hashtable: 임계값 초과 시 자동 전환. 키-값 쌍을 해시 테이블로 저장.

Terminal window
# 임계값 확인/변경
CONFIG GET hash-max-ziplist-entries # → 128
CONFIG SET hash-max-ziplist-entries 64
# 인코딩 확인
HSET user:1 name "Alice" age 30
OBJECT ENCODING user:1 # → ziplist (항목 수 적음)

실무 튜닝 포인트: 작은 해시 수백만 개를 저장할 때 ziplist 임계값을 늘리면 메모리를 30~50% 절약할 수 있다. 단, 조회 시간은 O(N)으로 증가하므로 항목 수와 트레이드오프를 고려한다.


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) — 전체 조회 비용 주의

intset: 모든 요소가 정수이고 64개 이하일 때 사용. 정렬된 정수 배열로 이진 탐색.

listpack: 소규모 문자열 셋에서 사용 (Redis 7.2+).

hashtable: 요소가 많거나 문자열이 길면 전환.

Terminal window
SADD intset-example 1 2 3 4 5
OBJECT ENCODING intset-example # → intset
SADD str-example "apple" "banana"
OBJECT ENCODING str-example # → listpack 또는 hashtable

Sorted 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)인 이유다.

Terminal window
# 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 레벨)
이벤트 루프 (단일 스레드)
커맨드 큐 → 순차 실행 → 응답 반환

핵심 원리:

  1. 비블로킹 I/O: epoll(Linux) / kqueue(macOS)로 수천 개의 소켓을 동시에 감시. 데이터가 준비된 소켓만 처리.
  2. 메모리 연산: 디스크 I/O 없이 RAM에서 직접 연산 → 마이크로초 단위 응답.
  3. 단일 스레드의 장점: 락(Lock) 없음 → 컨텍스트 스위칭 오버헤드 없음 → 모든 커맨드가 원자적(Atomic).
싱글 스레드라 Lock 없음
→ INCR이 항상 원자적 (Read-Modify-Write가 끊기지 않음)
→ MULTI/EXEC 트랜잭션이 단순하게 동작
약점대응 방법
CPU 코어 하나만 사용여러 Redis 인스턴스 실행 (포트별)
블로킹 커맨드(KEYS *, SORT 대용량)SCAN으로 대체, 대용량 연산 분리
대용량 데이터 직렬화 비용Pipeline으로 네트워크 왕복 감소

Redis 6.0+ I/O 스레드: 네트워크 읽기/쓰기는 멀티스레드로 처리하되, 커맨드 실행 자체는 여전히 단일 스레드. 처리량이 20~30% 향상.

Terminal window
# redis.conf
io-threads 4 # I/O 스레드 수 (CPU 코어 수보다 작게)
io-threads-do-reads yes

Redis는 기본적으로 메모리 저장소다. 재시작 시 데이터를 복구하려면 영속화(Persistence)가 필요하다.

특정 시점의 전체 데이터를 바이너리 파일(.rdb)로 덤프.

Terminal window
# redis.conf
save 900 1 # 900초(15분) 내에 1번 이상 변경 시 저장
save 300 10 # 300초(5분) 내에 10번 이상 변경 시 저장
save 60 10000 # 60초 내에 10,000번 이상 변경 시 저장
dbfilename dump.rdb
dir /var/lib/redis

RDB 저장 메커니즘 — fork + Copy-On-Write:

메인 프로세스 (서비스 계속)
↓ fork()
자식 프로세스 → RDB 파일 작성 (부모 메모리를 복사하지 않고 참조)
↓ Copy-On-Write
부모가 데이터 수정 시에만 해당 페이지 복사
  • fork() 시점에 메모리 전체를 복사하지 않음 → 메모리 효율적
  • 자식이 쓰는 동안 부모는 계속 요청 처리
  • fork() 자체는 짧은 블로킹 발생 (데이터 크기에 비례)

RDB 장점/단점:

항목내용
장점파일 크기 작음, 복구 빠름, 성능 영향 적음
단점마지막 스냅샷 이후 데이터 손실 가능 (최대 수분)
적합주기적 백업, 데이터 손실 허용 가능한 캐시 용도

모든 쓰기 커맨드를 순서대로 파일에 기록. 재시작 시 파일을 replay하여 복구.

Terminal window
# redis.conf
appendonly yes
appendfilename "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 → ... (수천 줄)
↓ BGREWRITEAOF
SET a 3 (현재 상태만 기록, 중간 과정 제거)
Terminal window
# 자동 Rewrite 설정
auto-aof-rewrite-percentage 100 # 파일 크기가 2배 될 때
auto-aof-rewrite-min-size 64mb # 최소 64MB 이상일 때

RDB + AOF 혼합 모드 (Redis 4.0+, 권장)

섹션 제목: “RDB + AOF 혼합 모드 (Redis 4.0+, 권장)”
Terminal window
# redis.conf
aof-use-rdb-preamble yes # AOF 파일의 앞부분은 RDB 형식으로 저장
  • 재시작 시 RDB로 빠르게 로드 → 이후 AOF 로그만 replay → 빠른 복구 + 낮은 데이터 손실
  • BullMQ 등 작업 큐를 Redis에 저장할 때 반드시 AOF 활성화 필요
상황권장 설정
순수 캐시 (손실 허용)RDB만 (AOF 비활성화)
세션 저장 (최대 1초 손실 허용)AOF everysec
BullMQ 큐 (손실 불허)AOF always 또는 혼합
실시간 랭킹 (재연산 가능)RDB만

단일 Redis 인스턴스의 메모리 한계를 넘어 수평 확장하는 방법.

총 16,384개의 슬롯 → N개의 마스터 노드에 균등 분배
키 → CRC16(key) % 16384 = 슬롯 번호 → 해당 슬롯을 가진 노드로 라우팅
예시 (3 마스터):
노드 A: 슬롯 0 ~ 5460
노드 B: 슬롯 5461 ~ 10922
노드 C: 슬롯 10923 ~ 16383
Terminal window
# 슬롯 확인
redis-cli --cluster check [primary-endpoint]:6379
# 특정 키가 몇 번 슬롯인지 확인
redis-cli CLUSTER KEYSLOT "product:123" # → e.g., 7638 (노드 B 담당)
일반 키: 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 에러 없음
클라이언트 → 노드 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 마스터 권장)

클러스터 모드 없이 단일 Primary + Replica 구성에서 자동 Failover를 제공한다.

Sentinel 1 Sentinel 2 Sentinel 3
↓ ↓ ↓
Primary Redis
Replica Redis

Sentinel 역할:

  1. Monitoring: Primary/Replica 상태를 주기적으로 확인
  2. Notification: 장애 감지 시 알림
  3. Automatic Failover: Primary 장애 시 Replica를 자동 승격
  4. Configuration Provider: 클라이언트가 현재 Primary 주소를 Sentinel에 질의
Terminal window
# sentinel.conf
sentinel 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,
});
항목SentinelCluster
사용 목적HA (고가용성)HA + 수평 확장
데이터 분산없음 (모든 데이터가 Primary에)있음 (16,384 슬롯으로 분산)
메모리 한계단일 노드 메모리노드 수 × 단일 노드 메모리
구성 복잡도낮음높음
다중 키 연산제한 없음동일 슬롯 키만 (CROSSSLOT 주의)
적합한 경우수십~수백 GB 이하, 단순 구성수 TB, 초고처리량

AWS ElastiCache는 Redis를 관리형으로 제공한다. 핵심 튜닝 포인트를 정리한다.

Terminal window
# 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 = 128
hash-max-listpack-value = 64
zset-max-listpack-entries = 128
zset-max-listpack-value = 64
워크로드권장 노드 타입이유
개발/소규모cache.t3.micro최소 비용
일반 캐시 (수십 GB)cache.r6g.large메모리 최적화, Graviton2
고처리량 (초당 수십만 요청)cache.r6g.xlarge 이상네트워크 대역폭 충분
BullMQ/세션 (영속화 필수)cache.r6g.* + AOF메모리 여유 필요 (AOF 오버헤드)
지표정상 범위경보 기준
EngineCPUUtilization< 50%> 80% 지속 시 알람
DatabaseMemoryUsagePercentage< 75%> 80% 시 즉시 대응
CacheHitRate> 90%< 80% 지속 시 캐시 전략 점검
CurrConnections안정적급증 시 연결 누수 점검
Evictions0 (캐시 외 용도)증가 시 메모리 부족 신호
ReplicationLag< 1초지속 증가 시 복제 지연 점검
Terminal window
# 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)
},
);

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 참고

항목RedisMemcached
자료구조String, Hash, List, Set, ZSet 등단순 String만
영속화RDB + AOF 지원없음 (재시작 시 전체 손실)
클러스터링내장 Cluster 모드클라이언트 사이드 샤딩만
멀티스레드싱글 스레드 (I/O는 멀티)완전 멀티스레드
Pub/Sub지원미지원
Lua 스크립트지원미지원
메모리 효율자료구조별 오버헤드 있음순수 Key-Value로 효율적
적합한 경우캐시 + 큐 + 세션 + 랭킹초대규모 단순 캐시 (멀티코어 활용)

  • 각 자료구조의 인코딩 전환 임계값을 알고 있는가? (OBJECT ENCODING 확인)
  • 단일 스레드 특성상 블로킹 커맨드(KEYS *, 대용량 SMEMBERS)를 피하는가?
  • SCAN을 사용하여 대용량 키 순회를 비블로킹으로 처리하는가?
  • BullMQ 사용 시 AOF가 활성화되어 있는가?
  • appendfsync everysec 이상으로 설정되어 있는가?
  • 혼합 모드(aof-use-rdb-preamble yes)를 사용하여 복구 속도를 최적화했는가?
  • 멀티 키 커맨드(MGET, Pipeline)에서 CROSSSLOT 에러를 처리했는가?
  • Hash Tag를 사용하여 관련 키를 동일 슬롯에 배치했는가?
  • Sentinel 또는 Cluster 구성에서 Failover 시나리오를 테스트했는가?
  • maxmemory-policy를 용도에 맞게 설정했는가?
  • reserved-memory-percent를 AOF + 복제 오버헤드에 맞게 설정했는가?
  • CloudWatch 주요 지표 알람을 설정했는가?
  • 읽기 부하 분산을 위해 Reader Endpoint를 활용하는가?

키워드설명
SDSSimple Dynamic String — Redis의 문자열 내부 구현체
ziplist / listpack소규모 자료구조를 위한 압축 인코딩 형식
Skip ListSorted Set의 범위 쿼리를 O(log N)으로 처리하는 자료구조
I/O Multiplexingepoll/kqueue로 다수 소켓을 단일 스레드에서 처리하는 기법
RDBRedis Database — 특정 시점의 스냅샷 파일
AOFAppend Only File — 모든 쓰기 커맨드를 순서대로 기록하는 영속화
BGSAVE백그라운드 RDB 저장 (fork + Copy-On-Write)
BGREWRITEAOF백그라운드 AOF 재작성 (파일 크기 압축)
Hash SlotRedis Cluster의 데이터 분산 단위 (총 16,384개)
CRC16키를 슬롯 번호로 매핑하는 해시 함수
Hash Tag{tag}:key 형식으로 동일 슬롯 강제 지정
MOVED클라이언트가 다른 노드로 재요청해야 할 때 반환되는 에러
ASK슬롯 이동 중(Resharding) 임시 리다이렉션 에러
SentinelRedis Primary 장애 시 자동 Failover를 수행하는 고가용성 컴포넌트
Configuration EndpointElastiCache 클러스터 모드의 단일 진입점
Reader EndpointElastiCache 비클러스터 모드의 읽기 전용 엔드포인트
reserved-memory-percentAOF/복제를 위해 예약하는 메모리 비율
EngineCPUUtilizationRedis 프로세스의 CPU 사용률 (CloudWatch 지표)
CacheHitRate캐시 적중률 (CloudWatch 지표)


Terminal window
docker run -d --name redis-intern -p 6379:6379 redis:7-alpine
docker exec -it redis-intern redis-cli
# String 인코딩
SET num 42
OBJECT ENCODING num # → int
SET short "hello"
OBJECT ENCODING short # → embstr
SET long "$(python3 -c "print('a'*50)")"
OBJECT ENCODING long # → raw
# Hash 인코딩 (ziplist → hashtable 전환 관찰)
HSET small-hash a 1 b 2
OBJECT ENCODING small-hash # → listpack (또는 ziplist)
# 128개 이상 필드 추가
for i in $(seq 1 130); do redis-cli HSET big-hash field$i value$i; done
OBJECT ENCODING big-hash # → hashtable
# Sorted Set 인코딩
ZADD small-zset 1 "a" 2 "b"
OBJECT ENCODING small-zset # → listpack
Terminal window
# 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 6379
SET 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 BGSAVE
docker exec redis-aof ls -la /data/ # dump.rdb 파일 생성 확인
Terminal window
# 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
Terminal window
# 로컬 클러스터 시뮬레이션
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 yes
done
# 클러스터 생성
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 INFO
docker exec -it redis-7001 redis-cli CLUSTER NODES
# Hash Tag 실습
docker exec -it redis-7001 redis-cli -c
CLUSTER KEYSLOT "{user}:123"
CLUSTER KEYSLOT "{user}:456"
# 같은 슬롯 번호인지 확인
MSET "{user}:123" "alice" "{user}:456" "bob" # CROSSSLOT 없음

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를 참고한다.