Stop 20 · System DesignTopics · 11/15

Distributed Caching

Part of the Backend Developer Roadmap

In short

Your API runs on six servers. If each keeps its own in-memory cache, they disagree with each other. A distributed cache like Redis gives all of them one shared, fast memory.

Running a shared cache (like a Redis cluster) accessible by all your server instances, instead of separate in-memory caches per instance that can go out of sync with each other.

The purple blob fills a glass at an office water cooler, with a water tank on the roof behind.

Think of it like this

Water coolers on every floor

The building's main tank is on the roof. Nobody climbs up for every glass. Each floor has a water cooler refilled from the tank. It's fast and close, but when the tank water changes, the coolers need refilling too. Caches are the coolers, the database is the tank.

Watch it happen

Cache-aside in action

API
Redis
Postgres

GET product:42

1/6A request needs product 42. The API checks Redis first.

The big ideas

What you need to know

Shared across servers

Every app instance sees the same cache, so a value cached by server 1 also helps server 6.

Always set a TTL

Expiry limits how stale data can get and stops forgotten keys from filling memory.

Invalidation is the hard part

When data changes, delete or update the cached key. Get this wrong and users see old prices.

Watch for stampedes

When a hot key expires, hundreds of requests can hit the database at once. Use a lock or refresh early.

See it in code

Walk through the code

ts
1import { createClient } from "redis";2const redis = await createClient({ url: process.env.REDIS_URL }).connect();3 4export async function getProduct(id: number) {5  const key = `product:${id}`;6  const cached = await redis.get(key);7  if (cached) return JSON.parse(cached);8  const product = await db.products.findById(id);9  await redis.set(key, JSON.stringify(product), { EX: 300 });10  return product;11}12 13export async function updatePrice(id: number, price: number) {14  await db.products.update(id, { price });15  await redis.del(`product:${id}`);16}

1/3Try Redis first. A hit returns immediately without touching the database.

This pattern is called cache-aside: the app reads the cache first and fills it on a miss. The database stays the source of truth.

Save yourself the pain

Common mistakes

Oops

Caching with no expiry 'because the data rarely changes'.

Do this instead

One missed invalidation then leaves wrong data forever. Always set a TTL as a safety net.

Oops

Treating Redis as the only copy of important data.

Do this instead

Caches can evict or lose keys. The database stays the source of truth.

Your mission

Cache a slow endpoint

~30 min

Quick check

Each of your 6 servers keeps its own in-memory cache. An admin updates a price. What's the risk?

Go deeper

  1. 1Compare a distributed cache (shared Redis) to per-instance in-memory caching for a multi-server app.
  2. 2Learn why cache consistency across instances is a real challenge at scale.
  3. 3Understand distributed caches also need their own scaling and replication strategy.

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge