DrawLintDrawLint.ai
🗺️Design Patterns·6 min read

Redis as Cache & Data Structures

In-memory store for caching, counters, presence, rate limits, and seat holds.

Redis is an in-memory data store used for very fast access to hot data and short-lived coordination state. People call it a cache, but that undersells it: Redis gives you strings, hashes, lists, sets, sorted sets, streams, HyperLogLogs, and bitmaps with atomic operations that solve problems a plain key-value cache cannot.

🔭Think of it like…
Redis is the chef's mise en place: small bowls of prepped ingredients within arm's reach. The pantry is still the durable database, but the chef keeps the next few minutes of high-value ingredients on the counter, arranged in containers that match how each ingredient will be used.

The problem: the source of truth is too slow for hot paths

Your relational database or document store should remain authoritative, but not every request deserves a trip there. Recomputing a home feed, checking whether a user exceeded an API quota, loading a leaderboard, or holding a concert seat for five minutes can be too latency-sensitive or too write-noisy for the source-of-truth database.

hot path without Redis
GET /product/42
  ├─ query product row
  ├─ query inventory summary
  ├─ query review count
  ├─ query promotion rules
  └─ render page after several database round trips

Redis moves carefully chosen hot data into memory and lets the application operate on it with single-command atomicity. The failure mode to remember is that memory is finite and usually not the ultimate source of truth. If you put everything in Redis forever, you have built an expensive and fragile database without the durability guarantees you probably need.

The core idea
Use Redis for hot, derived, or short-lived state where microsecond-to- low-millisecond access and atomic data-structure operations matter. Keep durable facts in a real source-of-truth database unless you have explicitly designed Redis persistence and recovery.

How Redis works: memory plus a single-threaded event loop

Redis keeps its primary working set in RAM and processes commands through an event loop. Most commands are small and execute quickly, so a single thread can serve many clients without lock contention. This design is one reason Redis feels so fast, but it also means one slow command can block other clients on that shard.

cache-aside read path
GET /users/42
  ├─ redis GET user:42
  │    ├─ hit  → return cached JSON
  │    └─ miss → query database
  │             redis SET user:42 <json> EX 300
  │             return database row

Why single-threaded does not mean low capacity

Network I/O is event-driven, data is in memory, and operations such asINCR, HGET, and ZADD are compact. Throughput comes from doing tiny operations predictably. The danger is using commands that scan huge keys, running heavy Lua scripts, or storing giant values that make one event-loop turn take too long.

PropertyBenefitGotcha
In-memorySub-ms access to hot dataRAM cost and eviction pressure
Single event loopAtomic simple commands without locksSlow commands block the shard
TTL supportNatural expiry for holds, sessions, and cache entriesSynchronized expiry can cause stampedes
Rich typesOperations match product featuresWrong type choice creates awkward code

Core data structures and what they are for

Redis is most valuable when you choose the data structure that matches the access pattern. A leaderboard is not just a cached JSON blob; it is a sorted set. Presence is not just a table scan; it is a set or short TTL key per online user.

Data structureExample commandUse case
StringGET / SET / INCRCached JSON, counters, feature flags, seat hold token
HashHGET / HSETUser profile fields, session attributes, shopping cart metadata
ListLPUSH / BRPOPSimple queues, recent activity, worker handoff
SetSADD / SISMEMBERUnique online users, tags, membership checks, mutual friends
Sorted setZADD / ZRANGELeaderboards, priority queues, expiring holds ordered by deadline
StreamXADD / XREADGROUPDurable-ish event log, background job fan-out, audit tail
HyperLogLogPFADD / PFCOUNTApproximate unique visitors with tiny memory
BitmapSETBIT / BITCOUNTDaily active flags, attendance, feature rollout bitsets
leaderboard with a sorted set
ZADD leaderboard:weekly 1842 user:9
ZADD leaderboard:weekly 2210 user:12
ZREVRANGE leaderboard:weekly 0 9 WITHSCORES
ZRANK leaderboard:weekly user:9
Atomic operations are the hidden superpower
A single Redis command is atomic on its shard. That makes counters, membership checks, sorted-set updates, and TTL-based holds much simpler than a read-modify-write loop against a slower database.

Flagship uses: cache, limiter, leaderboard, presence, hold, lock

Redis appears in many architectures because the same primitives combine into common product features. The trick is to be explicit about which features are derived and rebuildable versus which ones require careful persistence and reconciliation.

fixed-window rate limiter
key = "rate:user:42:2026-06-10T07:16"
count = INCR key
if count == 1:
  EXPIRE key 60
if count > 100:
  reject request with 429
UseRedis primitiveDesign note
CacheString or hash + TTLUse cache-aside, jittered TTLs, and single-flight rebuilds
Rate limiterINCR + EXPIRE or sorted setChoose fixed window, sliding window, or token bucket
LeaderboardSorted setScore updates and rank queries are natural
PresenceSet or TTL keyExpire automatically when heartbeats stop
Seat holdSET key token NX EX 300TTL releases abandoned holds without a cron job
Distributed lockSET resource token NX PX ttlUse fencing tokens for correctness-critical work

Seat hold example

hold one seat for five minutes
SET seat:show123:A7 hold_8f31 NX EX 300
-- success: user owns a temporary hold
-- nil: somebody else already holds or bought the seat

-- checkout must still confirm in the database transaction
-- before charging, because Redis is not the ticket ledger
Related patterns
For lock safety, especially fencing tokens and failure cases, see Distributed Locking. For cache-aside, write-through, TTLs, and stampedes, see Caching Systems.

Persistence, replication, and cluster mode

Redis can persist data, but you must choose what kind of recovery you need. RDB snapshots write compact point-in-time dumps.AOF logs write commands so Redis can replay them after a restart. Many systems enable both: RDB for fast snapshots and AOF for a smaller loss window.

FeatureWhat it doesTrade-off
RDB snapshotPeriodically saves the datasetFast restart, but changes since last snapshot can be lost
AOFAppends each write commandBetter durability window, more disk I/O
ReplicaCopies a primary for reads or failoverAsync lag can lose recent writes on promotion
ClusterShards keyspace across primariesMulti-key operations need keys in the same hash slot

Redis Cluster splits keys across hash slots. This increases total memory and throughput, but it changes how you design keys. Operations involving multiple keys are easiest when those keys live in the same slot, often by using a hash tag such as cart:{user42}:itemsand cart:{user42}:meta.

Persistence does not make every Redis use safe
AOF and RDB improve recovery, but they do not automatically give you relational constraints, durable cross-key transactions, or immunity from split-brain failover. For money, inventory ownership, and legal records, write the final truth to the authoritative database.

Edge cases and real-world failure modes

  • Cache stampede: when a hot key expires, many requests may rebuild it at once. Use single-flight, request coalescing, stale-while- revalidate, and jittered TTLs.
  • Eviction surprises: if memory fills, Redis may evict keys according to policy. Do not store non-rebuildable facts in a cache database with eviction enabled.
  • Big keys: a huge list, hash, or string can block the event loop during serialization, deletion, or transfer. Prefer bounded keys and incremental deletion where possible.
  • TTL semantics: expired keys disappear eventually, not as a real-time scheduler guarantee. Use streams or a database job for must-run workflows.
  • Failover windows: primary-replica replication is commonly asynchronous, so a promoted replica may miss the last writes unless your design tolerates or reconciles them.

Real-world examples include caching product cards for an ecommerce homepage, using sorted sets for a gaming leaderboard, storing presence heartbeats for collaborative editing, and using short TTL keys to hold seats while checkout completes in the relational database.

Key takeaways
  • Redis is an in-memory, event-loop-driven store for hot data and atomic operations on rich data structures.
  • Choose the right type: strings for cached values, hashes for objects, sets for membership, sorted sets for ranking, streams for logs, and probabilistic or bit structures for compact counting.
  • Flagship patterns include cache-aside, rate limiting, leaderboards, presence, TTL seat holds, and distributed locks.
  • RDB and AOF provide persistence options, while replicas and Cluster provide availability and scale with important consistency trade-offs.
  • Keep the durable source of truth elsewhere unless you have intentionally designed Redis durability, failover, and reconciliation around the data.
A sorted set stores each member with a score and supports atomic updates, rank queries, and top-N reads. A JSON array would require reading, modifying, sorting, and writing the whole structure for every score change.
Redis stores a temporary key with NX and a TTL so only one user can hold the seat briefly and abandoned holds release automatically. The final purchase still verifies and records ownership in the authoritative database transaction.
Many requests miss the cache at the same time and all hit the database to rebuild the same value. Use single-flight rebuilds, jittered TTLs, stale-while-revalidate, and proactive warming for the hottest keys.
Finished this lesson?

Mark it complete to track your progress through the workbook.