← Back to Blog

[Database] Redis

computer science > database

2026-07-065 min read

#computer-science #database #redis #cache #in-memory-database

Redis는 Remote Dictionary Server의 약자로, key-value model을 기반으로 하는 in-memory data structure store이다. 일반적인 disk-based database와 달리 주요 데이터를 메모리에 올려두고 처리하므로 latency가 낮고 throughput이 높다.

Redis는 단순한 cache라기보다 다음 용도로 사용될 수 있다.

용도설명
CacheDB query 결과나 API 응답을 임시 저장한다.
Session store여러 웹 서버가 같은 session state를 공유한다.
Counter조회수, rate limit, token bucket 등을 빠르게 갱신한다.
Queuelist나 stream을 이용해 작업 큐를 구성한다.
Rankingsorted set으로 leaderboard를 만든다.
Pub/Sub간단한 message broadcasting을 구현한다.

Redis가 빠른 이유

Redis가 빠른 가장 직접적인 이유는 대부분의 연산이 memory에서 수행되기 때문이다. Disk I/O를 매번 기다리지 않아도 되므로 single operation latency가 낮다.

또 하나의 특징은 Redis command execution이 기본적으로 single-threaded event loop에서 처리된다는 점이다. 이 구조는 한 번에 하나의 command를 순차적으로 처리하므로, 개별 command 단위의 atomicity를 이해하기 쉽다.

예를 들어 두 client가 동시에 다음 명령을 보낸다고 하자.

INCR page:view

Redis는 이를 섞어서 실행하지 않고 command 단위로 순서대로 처리한다. 따라서 counter update에서 read-modify-write race condition을 application layer에서 직접 처리하지 않아도 된다.

다만 single-threaded라는 말이 항상 CPU 하나만 쓴다는 뜻은 아니다. Redis는 I/O threading, background persistence, replication 등에서 여러 thread/process를 사용할 수 있다. 핵심은 data mutation command가 단순하고 예측 가능한 execution model을 가진다는 점이다.


Key Naming

Redis의 key는 string이다. 실무에서는 namespace를 명확히 하기 위해 colon을 자주 사용한다.

user:1001
session:abc123
post:42:view_count
rate-limit:login:192.168.0.10

좋은 key naming은 운영에서 중요하다. Redis는 key-value store이기 때문에 schema가 느슨하다. 따라서 key 이름이 사실상 schema 역할을 한다.

패턴의미
user:{id}특정 user 객체
post:{id}:viewspost별 조회수
session:{token}session token별 상태
cache:{resource}:{id}cache entry

Strings

String은 Redis의 가장 기본적인 data type이다. 문자열뿐 아니라 숫자, JSON string, serialized object도 저장할 수 있다.

SET username "minsup"
GET username

Counter로 사용할 때는 atomic increment가 가능하다.

SET post:1:views 0
INCR post:1:views
INCRBY post:1:views 10

Application에서 다음처럼 구현하면 race condition이 생길 수 있다.

value = GET key
value = value + 1
SET key value

대신 Redis command 하나로 처리하면 atomic하다.

INCR key

Expiration

Redis cache에서 중요한 기능은 TTL(Time To Live)이다. 특정 key가 일정 시간이 지나면 자동으로 삭제되도록 설정할 수 있다.

SET session:abc123 "user-1" EX 3600

또는 이미 존재하는 key에 expiration을 붙일 수도 있다.

EXPIRE session:abc123 3600
TTL session:abc123

TTL은 cache invalidation을 단순하게 만든다. 예를 들어 API response cache를 5분만 유지하려면 다음처럼 쓸 수 있다.

SET cache:post:42 "{...}" EX 300

Lists

List는 insertion order가 있는 문자열 sequence이다. 왼쪽과 오른쪽 양 끝에서 push/pop할 수 있다.

LPUSH jobs "send-email"
LPUSH jobs "resize-image"
RPOP jobs

이 패턴은 queue처럼 동작한다.

Command의미
LPUSH왼쪽에 추가
RPUSH오른쪽에 추가
LPOP왼쪽에서 제거
RPOP오른쪽에서 제거
BLPOP값이 생길 때까지 blocking pop

간단한 worker queue는 다음 구조로 만들 수 있다.

LPUSH queue:email job-1
BRPOP queue:email 0

다만 list 기반 queue는 복잡한 retry, ack, consumer group이 필요해지면 한계가 있다. 그런 경우 Redis Streams나 전용 message queue를 검토하는 것이 낫다.


Sets

Set은 중복 없는 unordered collection이다. tag, membership, unique visitor 등에 유용하다.

SADD post:1:tags redis database cache
SMEMBERS post:1:tags
SISMEMBER post:1:tags redis

Set operation도 지원한다.

SINTER post:1:tags post:2:tags
SUNION post:1:tags post:2:tags
SDIFF post:1:tags post:2:tags

예를 들어 특정 날짜의 unique visitor를 저장할 수 있다.

SADD uv:2026-07-06 user-1
SADD uv:2026-07-06 user-2
SCARD uv:2026-07-06

Sorted Sets

Sorted Set은 member마다 score를 가진 set이다. Member는 중복되지 않고, score 기준으로 정렬된다.

ZADD leaderboard 1500 alice
ZADD leaderboard 2200 bob
ZADD leaderboard 1800 charlie

상위 랭킹은 다음처럼 조회한다.

ZREVRANGE leaderboard 0 2 WITHSCORES

특정 사용자의 rank도 계산할 수 있다.

ZREVRANK leaderboard bob
ZSCORE leaderboard bob

Sorted set은 다음 문제에 잘 맞는다.

문제이유
게임 leaderboardscore 기준 정렬이 필요하다.
priority queue낮거나 높은 score를 우선 처리할 수 있다.
time window rankingtimestamp를 score로 둘 수 있다.
rate limitrequest timestamp를 score로 저장할 수 있다.

Hashes

Hash는 field-value pair를 저장하는 data type이다. 하나의 object를 Redis key 하나에 담을 때 유용하다.

HSET user:1 name "Minsup" level "gold"
HGET user:1 name
HGETALL user:1

Relational database의 row처럼 생각할 수 있지만, Redis hash는 query language를 제공하지 않는다. 즉 다음과 같은 query는 Redis hash만으로 자연스럽지 않다.

SELECT * FROM users WHERE level = 'gold';

Redis에서는 access pattern에 맞춰 key를 설계해야 한다. user:{id}로 직접 조회하는 구조에는 hash가 잘 맞지만, 임의 조건 검색에는 RDBMS나 search engine이 더 적합하다.


Persistence

Redis는 in-memory store이지만 persistence 옵션을 제공한다. 대표적인 방식은 RDB와 AOF이다.

방식설명장점단점
RDB특정 시점 snapshot을 저장파일이 compact하고 복구가 빠름마지막 snapshot 이후 데이터 유실 가능
AOFwrite command log를 append데이터 유실 가능성이 낮음파일이 커질 수 있고 replay 비용이 있음

RDB는 snapshot이다.

현재 memory state -> dump.rdb

AOF는 command log이다.

SET a 1
INCR counter
HSET user:1 name minsup

Cache 용도로만 쓴다면 persistence를 약하게 잡거나 꺼도 된다. 반대로 Redis를 primary storage처럼 사용한다면 persistence, replication, backup 전략을 신중하게 잡아야 한다.


Cache-Aside Pattern

Redis를 cache로 사용할 때 가장 흔한 패턴은 cache-aside이다.

1. Client가 데이터를 요청한다.
2. Application이 Redis에서 먼저 찾는다.
3. Cache hit이면 바로 반환한다.
4. Cache miss이면 DB에서 조회한다.
5. DB 결과를 Redis에 저장한다.
6. Client에게 반환한다.

Pseudo code로 쓰면 다음과 같다.

const cached = await redis.get(cacheKey)
if (cached !== null) {
  return JSON.parse(cached)
}

const value = await db.posts.findById(postId)
await redis.set(cacheKey, JSON.stringify(value), { EX: 300 })
return value

이 패턴에서 중요한 문제는 invalidation이다. DB 값이 바뀌었는데 Redis cache가 오래 살아 있으면 stale data를 반환할 수 있다. 이를 줄이는 방법은 크게 세 가지다.

방법설명
짧은 TTL오래된 값이 오래 남지 않게 한다.
write 시 deleteDB update 후 관련 cache key를 삭제한다.
versioned keydata version을 key에 포함한다.

Redis를 쓰면 안 좋은 경우

Redis가 빠르다고 해서 모든 데이터를 Redis에 넣는 것이 좋은 것은 아니다.

상황이유
복잡한 ad-hoc queryRedis는 SQL database가 아니다.
큰 object를 많이 저장memory 비용이 커진다.
강한 transaction/constraint 필요RDBMS가 더 적합하다.
영구 저장이 핵심persistence 설계를 매우 신중히 해야 한다.
access pattern이 불명확key design이 어려워진다.

Redis는 보통 primary database를 대체하기보다, read path와 coordination path를 빠르게 만드는 보조 storage로 가장 많이 쓰인다.


정리

Redis의 핵심은 단순한 key-value cache가 아니라, memory 위에서 동작하는 data structure server라는 점이다.

Data type대표 용도
Stringcache, counter, token
Listsimple queue
Setmembership, unique count
Sorted Setranking, priority queue
Hashobject field storage

Redis를 잘 쓰려면 command를 많이 아는 것보다 access pattern을 먼저 정해야 한다.

Redis schema는 table definition이 아니라 key naming과 data structure 선택에서 나온다.

따라서 Redis를 설계할 때는 항상 다음 질문을 먼저 해야 한다.

이 데이터는 어떤 key로 읽히는가?
얼마나 오래 살아야 하는가?
stale data가 허용되는가?
어떤 Redis data structure가 이 access pattern에 맞는가?