Every database query costs something — disk I/O, CPU, time. If a thousand people load the same product page in the same minute, a plain app re-runs the exact same expensive query a thousand times, for an answer that didn’t change at all between request #1 and request #1000. Caching is just refusing to do that: keep the answer sitting in memory, and hand out the copy instead of recomputing it. Redis is the tool most backends reach for to do this — an in-memory key-value store built to answer in well under a millisecond.
What Redis actually is
Redis (REmote DIctionary Server) is a key-value store that lives almost entirely in RAM, which is what makes it so fast — reading from memory is roughly 50–100x faster than reading from disk-backed storage like Postgres or MySQL. Unlike a plain dictionary though, Redis understands several data shapes natively: strings, hashes, lists, sets, and sorted sets — so it isn’t just a cache, it’s a small, extremely fast database that most teams happen to use as a cache.
Every lookup is either a hit or a miss
Cache miss
- Redis has nothing stored under that key
- The server has to fall back to the real database
- Slower — this request pays the full cost
- Ends with the server writing the result into Redis
Cache hit
- Redis already has the answer in memory
- The database is never touched
- Fast — often sub-millisecond
- This is the state you want almost every request in
The first request — a cache miss
Nobody has asked for this data recently, so Redis comes up empty and the server has to go the slow way, then save its work on the way out.
A request comes in for data the server has never been asked for before — or hasn't been asked for in a while.
Every request after — a cache hit
Same endpoint, same data — but now Redis already has it. Watch how much shorter this round trip is, and notice the database doesn’t appear in it at all.
The exact same request — this time Redis already has the answer from the last request that warmed it.
0.1–1ms
Typical Redis read
10–50ms+
Typical DB query
30s – 1hr
Common TTL range
How long should the data live?
That EX 300 in the miss diagram is a TTL — time to live, in seconds. It’s what keeps a cache from quietly turning into a database full of lies: without an expiry, the first write stays frozen in Redis forever, even after the real data changes underneath it. Short TTLs keep data fresher but cache less effectively; long TTLs cache better but risk staleness. Most teams start around 30 seconds to a few minutes for data that changes occasionally, and go much longer for data that’s basically static.
Cache invalidation — the actually hard part
There’s an old joke that there are only two hard problems in computer science: cache invalidation, naming things, and off-by-one errors. The TTL above is the lazy version — wait for the clock to run out. Two other strategies handle it more deliberately:
- Write-through invalidation — the moment the underlying data changes (an
UPDATEto that product), the server explicitly runsDEL product:42so the next read is forced to be a fresh miss. - Event-driven invalidation — a change anywhere in the system publishes an event (via Redis
PUBLISHor a message queue), and any service holding a cached copy clears it in response.
What happens when Redis runs out of memory
Redis has a configurable maxmemory limit. Once it’s full, it needs an eviction policy to decide what to throw away to make room for new writes:
- allkeys-lru — evict whatever hasn’t been read least recently, across every key. The most common default for pure caching workloads.
- volatile-lru — same idea, but only among keys that have a TTL set, leaving permanent keys untouched.
- allkeys-lfu — evict whatever is read least frequently overall, which handles “popular most of the time, but one huge burst” keys better than LRU does.
- noeviction — refuse new writes once full, and return errors instead. Appropriate when Redis is holding data you can’t afford to silently lose, not just cache.
Redis is more than just a cache
The same in-memory speed makes Redis a natural fit well beyond “remember this API response”:
- Session storage — exactly the shared session store from the sessions post: every app server reads the same Redis instance, so it doesn’t matter which server handles the next request.
- Rate limiting — a counter per user per time window, incremented atomically on every request, checked against a limit before the request is allowed through.
- Leaderboards — Redis’s sorted-set type keeps members ordered by score automatically, so “top 10 players right now” is a single
ZRANGEcall, not a sort over the whole table. - Pub/sub & queues — lightweight real-time messaging between services, or a simple job queue, without standing up a separate broker.