Redis는 풍부한 데이터 구조를 지원하는 인메모리 키-값 저장소입니다. 캐싱, 메시지 큐, 분산 잠금, 리더보드 등 다양한 용도로 활용됩니다. 이 가이드는 설치부터 시작해 모든 핵심 데이터 타입과 실무 사용법을 다룹니다.
설치 및 연결
macOS
brew install redis
brew services start redis # 백그라운드에서 시작
redis-cli # 대화형 클라이언트 열기
Linux(Ubuntu / Debian)
sudo apt update && sudo apt install redis-server
sudo systemctl start redis
redis-cli
Docker(권장 — 깔끔한 환경)
# 컨테이너 실행
docker run -d --name redis -p 6379:6379 redis:7-alpine
# CLI 열기
docker exec -it redis redis-cli
기본 연결 및 확인
redis-cli -h 127.0.0.1 -p 6379
# 연결 확인
PING # PONG 반환
# 비밀번호가 설정된 경우
AUTH your_password
# 데이터베이스 선택(기본값 0, 총 16개)
SELECT 1
# 현재 데이터베이스의 키 개수 확인
DBSIZE
# 모든 키 조회(운영 환경에서는 피할 것)
KEYS *
# 패턴 스캔(운영 환경에서는 KEYS 대신 SCAN 권장)
SCAN 0 MATCH user:* COUNT 100
데이터 타입 개요
| 타입 | 주요 사용 사례 |
|---|---|
| String | 캐시, 카운터, 분산 잠금 |
| Hash | 객체 저장(사용자 프로필, 상품 정보) |
| List | 메시지 큐, 활동 피드, 작업 로그 |
| Set | 중복 제거, 공통 친구, 추첨 |
| ZSet(정렬된 집합) | 리더보드, 가중치 큐 |
| Stream | 영속 메시지 큐(경량 Kafka 대체) |
| Bitmap | 사용자 출석 체크, 블룸 필터 |
| HyperLogLog | 대규모 UV 추정(근사값) |
String(문자열)
String은 가장 기본적인 타입으로 텍스트, 숫자, 바이너리 데이터를 저장할 수 있습니다(최대 512 MB).
# 저장 및 조회
SET name "Alice"
GET name # "Alice"
# 만료 시간과 함께 저장(초 단위)
SET token "abc123" EX 3600
# 키가 존재하지 않을 때만 설정(분산 잠금의 기초)
SET lock "1" NX EX 10
# 일괄 작업
MSET k1 v1 k2 v2 k3 v3
MGET k1 k2 k3
# 값에 추가
APPEND name " Smith" # "Alice Smith"
# 숫자 연산
SET counter 0
INCR counter # 1
INCRBY counter 5 # 6
DECR counter # 5
DECRBY counter 2 # 3
# 부동소수점 증가
SET price 9.99
INCRBYFLOAT price 0.5 # 10.49
# 문자열 길이 조회
STRLEN name
# 이전 값을 반환하고 새 값으로 설정
GETSET name "Bob" # "Alice Smith" 반환, "Bob"으로 설정
Hash(해시)
Hash는 구조화된 객체 저장에 적합하며 반복적인 직렬화/역직렬화를 피할 수 있습니다.
# 필드 설정
HSET user:1 name "Alice" age 25 email "alice@example.com"
# 단일 필드 조회
HGET user:1 name # "Alice"
# 모든 필드와 값 조회
HGETALL user:1
# 모든 필드 이름 조회
HKEYS user:1
# 모든 값 조회
HVALS user:1
# 필드 개수
HLEN user:1
# 필드 존재 여부 확인
HEXISTS user:1 email # 1(존재함)
# 필드 삭제
HDEL user:1 email
# 여러 필드 한 번에 조회
HMGET user:1 name age
# 숫자 필드 증가
HINCRBY user:1 age 1
# 필드가 없을 때만 설정
HSETNX user:1 role "admin"
List(리스트)
List는 양방향 연결 리스트로 양쪽 끝에서 push/pop을 지원합니다. 큐와 스택에 적합합니다.
# 왼쪽(헤드)에 추가
LPUSH logs "event1" "event2" "event3"
# 오른쪽(테일)에 추가
RPUSH queue "task1" "task2"
# 왼쪽에서 꺼내기
LPOP logs
# 오른쪽에서 꺼내기
RPOP queue
# 블로킹 꺼내기(0 = 무한 대기)
BLPOP queue 0
BRPOP queue 30 # 최대 30초 대기
# 범위 조회(0 -1 = 전체)
LRANGE logs 0 -1
# 인덱스로 조회
LINDEX logs 0
# 리스트 길이
LLEN logs
# 트림(앞 100개만 유지)
LTRIM logs 0 99
# 특정 요소 앞뒤에 삽입
LINSERT logs BEFORE "event2" "new-event"
# 일치하는 요소 삭제
LREM logs 2 "event1" # "event1" 2개 삭제
대표 활용: 메시지 큐
# 프로듀서
RPUSH queue "msg1"
# 컨슈머(블로킹 — 메시지 도착 즉시 반환)
BLPOP queue 0
Set(집합)
Set은 순서 없는 고유한 멤버들의 집합으로 집합 연산을 지원합니다.
# 멤버 추가
SADD tags "redis" "cache" "nosql"
# 멤버 존재 여부 확인
SISMEMBER tags "redis" # 1
# 모든 멤버 조회
SMEMBERS tags
# 집합 크기
SCARD tags
# 멤버 삭제
SREM tags "nosql"
# 랜덤 조회(삭제 없음)
SRANDMEMBER tags 2
# 랜덤 꺼내기(삭제)
SPOP tags
# 집합 연산
SADD set1 "a" "b" "c"
SADD set2 "b" "c" "d"
SUNION set1 set2 # 합집합: a b c d
SINTER set1 set2 # 교집합: b c
SDIFF set1 set2 # 차집합: a(set1에 있고 set2에 없는 것)
# 결과를 새 키에 저장
SUNIONSTORE result set1 set2
SINTERSTORE result set1 set2
ZSet(정렬된 집합)
ZSet의 각 멤버는 스코어를 가지며 스코어 순으로 정렬됩니다. 리더보드에 최적입니다.
# 멤버 추가(스코어 멤버명)
ZADD leaderboard 1000 "Alice" 850 "Bob" 1200 "Charlie"
# 멤버 스코어 조회
ZSCORE leaderboard "Alice" # 1000
# 스코어 오름차순 순위 조회(0 = 1위)
ZRANK leaderboard "Alice" # 1(0-indexed)
# 스코어 내림차순 순위 조회
ZREVRANK leaderboard "Alice" # 1
# 순위 범위로 조회(오름차순)
ZRANGE leaderboard 0 -1 WITHSCORES
# 순위 범위로 조회(내림차순)
ZREVRANGE leaderboard 0 2 WITHSCORES
# 스코어 범위로 조회
ZRANGEBYSCORE leaderboard 800 1100 WITHSCORES
# 스코어 증가
ZINCRBY leaderboard 100 "Alice" # 1100
# 전체 멤버 수
ZCARD leaderboard
# 스코어 범위 내 멤버 수
ZCOUNT leaderboard 900 1200
# 멤버 삭제
ZREM leaderboard "Bob"
# 순위 범위로 삭제
ZREMRANGEBYRANK leaderboard 0 1
# 스코어 범위로 삭제
ZREMRANGEBYSCORE leaderboard 0 800
만료 시간
# 초 단위로 만료 시간 설정
EXPIRE key 3600
# 밀리초 단위로 설정
PEXPIRE key 3600000
# Unix 타임스탬프(초)로 설정
EXPIREAT key 1893456000
# 남은 TTL 확인(초, -1=만료 없음, -2=키 없음)
TTL key
# 남은 TTL 확인(밀리초)
PTTL key
# 만료 시간 제거(영구 저장)
PERSIST key
영속성
Redis는 기본적으로 인메모리입니다. 재시작하면 데이터가 사라집니다. 두 가지 영속성 방법이 있습니다:
RDB(스냅샷)
주기적으로 메모리를 디스크에 덤프합니다. 기본 영속성 방식입니다.
# redis.conf(60초마다 1개 이상 변경 시 저장)
save 60 1
save 300 10
save 900 1
# 수동으로 트리거
BGSAVE # 비동기 백그라운드 저장
SAVE # 동기(블로킹 — 운영 환경에서 피할 것)
장점: 파일이 컴팩트하고 복원 속도가 빠름.
단점: 두 스냅샷 사이의 데이터가 손실될 수 있음.
AOF(추가 전용 파일)
모든 쓰기 명령을 기록하고 재시작 시 재생하여 데이터를 복원합니다.
# redis.conf
appendonly yes
appendfilename "appendonly.aof"
# 동기화 정책
appendfsync always # 매번 동기화 — 가장 안전, 가장 느림
appendfsync everysec # 초당 1회 동기화(권장 — 최대 1초 데이터 손실)
appendfsync no # OS에 맡김 — 가장 빠르지만 위험
장점: 안전성이 높고 최대 1초의 데이터 손실.
단점: 파일이 크고 복원이 느림.
하이브리드 영속성(권장 — Redis 4.0+)
# redis.conf
aof-use-rdb-preamble yes
AOF 파일 앞부분에 RDB 스냅샷을 포함하고 이후 증분 명령을 추가합니다. 속도와 안전성을 모두 확보합니다.
Pub/Sub
# 채널 구독(메시지를 기다리며 블로킹)
SUBSCRIBE news sports
# 메시지 발행
PUBLISH news "Breaking: Redis 8.0 released"
# 패턴 구독
PSUBSCRIBE news.*
# 구독 정보 확인
PUBSUB CHANNELS # 활성 채널 목록
PUBSUB NUMSUB news # 채널의 구독자 수
주의: Pub/Sub은 전송 후 잊어버리는 방식으로 영속성이 없습니다. 신뢰할 수 있는 메시지 전달에는 Stream이나 외부 MQ를 권장합니다.
트랜잭션
Redis 트랜잭션은 MULTI / EXEC를 사용해 명령 배치를 원자적으로 실행합니다(단, 런타임 오류에 대한 롤백은 없음).
MULTI # 트랜잭션 시작
SET k1 "v1" # 큐에 추가
SET k2 "v2"
INCR counter
EXEC # 큐의 모든 명령 실행
# 트랜잭션 취소
DISCARD
WATCH(낙관적 잠금):
WATCH balance # 키 감시
MULTI
DECRBY balance 100
EXEC # WATCH 이후 balance가 변경되면 nil 반환
Pipeline(배치 요청)
Pipeline은 여러 명령을 한 번의 네트워크 왕복으로 묶어 처리량을 크게 향상시킵니다.
# redis-cli 예시
redis-cli --pipe << EOF
SET k1 v1
SET k2 v2
INCR counter
EOF
주요 클라이언트 라이브러리는 모두 Pipeline을 지원합니다. Node.js(ioredis)예시:
const pipeline = redis.pipeline()
pipeline.set('k1', 'v1')
pipeline.set('k2', 'v2')
pipeline.incr('counter')
await pipeline.exec()
주요 사용 시나리오
캐싱
# Cache-Aside 패턴:
# 읽기: Redis 확인 → 미스 시 DB 조회 후 Redis에 저장
# 쓰기: DB 업데이트 후 Redis 캐시 삭제(일관성 보장)
SET user:1:profile "{...}" EX 300
분산 잠금
# 잠금 획득: NX로 단일 클라이언트만 성공 보장, EX로 데드락 방지
SET lock:order:123 "client-uuid" NX EX 30
# 잠금 해제: 내 잠금인지 확인 후 삭제(Lua 스크립트로 원자성 보장)
EVAL "
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
" 1 lock:order:123 client-uuid
레이트 리미팅
# 사용자당 1분에 100회 요청 제한
INCR rate:user:123
EXPIRE rate:user:123 60
# 원자적 초기화
SET rate:user:123 0 EX 60 NX # 첫 요청 시 초기화
INCR rate:user:123 # 요청마다 증가
리더보드
# 스코어 업데이트
ZINCRBY leaderboard 10 "user:123"
# Top 10 조회
ZREVRANGE leaderboard 0 9 WITHSCORES
# 사용자 순위 확인
ZREVRANK leaderboard "user:123"
세션 저장
SET session:abc123 "{userId: 1, role: admin}" EX 86400
GET session:abc123
EXPIRE session:abc123 86400 # 세션 갱신
캐시 3대 장애
캐시 침투(Cache Penetration)
증상: 캐시에도 DB에도 존재하지 않는 키에 대한 요청이 항상 DB를 직접 조회합니다.
해결책:
- null 값 캐싱: DB에 존재하지 않아도 짧은 TTL로 빈 값을 Redis에 저장
- 블룸 필터: 존재하지 않는 키에 대한 요청을 Redis 도달 전에 차단
캐시 장애(Cache Breakdown)
증상: 핫 키가 만료되는 순간 대량의 동시 요청이 일제히 DB를 조회합니다.
해결책:
- 핫 키를 영구적으로 유지(만료 없음 또는 논리적 만료 + 비동기 갱신)
- 뮤텍스 잠금: 첫 번째 요청만 캐시를 재구성하고 나머지는 대기
캐시 사태(Cache Avalanche)
증상: 대량의 키가 동시에 만료되어 DB가 순식간에 과부하 상태가 됩니다.
해결책:
- TTL에 랜덤 지터를 추가해 만료 시간을 분산
- 중요 데이터는 영구적으로 유지하고 비동기 백그라운드 갱신
- 서킷 브레이커와 그레이스풀 디그레이데이션으로 DB 보호
유용한 관리 명령
# Redis 정보 확인(메모리, 연결, 영속성 등)
INFO
INFO memory
INFO server
# 슬로우 쿼리 로그 확인
SLOWLOG GET 10
# 현재 데이터베이스 초기화(주의해서 사용)
FLUSHDB
# 모든 데이터베이스 초기화(주의해서 사용)
FLUSHALL
# 키 타입 확인
TYPE key
# 키 삭제
DEL key1 key2
# 비동기 삭제(큰 키에 권장, 논블로킹)
UNLINK key
# 키 이름 변경
RENAME old-key new-key
# 키 메모리 사용량 확인
MEMORY USAGE key
Fonnpo