Cachingcache-asideread-throughwrite-throughwrite-behindttl

Caching Patterns

Cache-aside, read-through, write-through and write-behind differ in who talks to whom and therefore in who is responsible for keeping the cache honest — and every one of them has a window where the cache is wrong.

▶ InteractiveInterview questionSee how this works internally →
Progress

Why cache at all

The same small fraction of data is read far more than it changes: the popular products, the logged-in user’s own profile, the rendered home page. Serving those from memory removes most of the database’s read load for a fraction of the cost of another replica, and cuts read latency from milliseconds to microseconds. The catch is that a cache is a second copy of the truth, and a second copy can disagree with the first.

The four patterns

Cache-aside (the default): the application checks the cache, and on a miss reads the database and populates the cache itself. On a write, it updates the database and deletes the cache key. Simple, the cache can fail without taking the app down, and only what is read gets cached. The burden: every write path must remember to invalidate, and a forgotten delete is a bug that looks like random staleness.

Read-through: the cache itself loads from the database on a miss, so the application only ever talks to the cache. Centralises the load logic, at the cost of the cache being in the critical path of every read. Write-through: writes go through the cache, which writes the database synchronously — read-after-write consistency, slower writes, and everything written is cached whether read or not. Write-behind: writes land in the cache and flush to the database in batches later — very fast writes, coalesced updates, and a durability risk if the cache dies before the flush.

TTL, eviction, and the metric that matters

A TTL bounds staleness: set it to the longest a value may safely be wrong. Eviction handles a full cache: allkeys-lru for a pure cache, and never the default noeviction, which turns a full cache into write failures. The number to watch is hit rate — hits over hits plus misses. Below ~80% the cache is barely helping and every miss still hits the database; above ~95% the database sees a fraction of the traffic. Hit rate is driven by TTL, by working-set size versus cache size, and by how skewed the access is.

Key points

  • Cache the reads that are frequent and tolerate being slightly old.
  • Cache-aside is the default: app populates on miss, deletes on write. Prefer delete over update on write.
  • Read-through centralises loads; write-through gives read-after-write; write-behind trades durability for write speed.
  • Set TTL to tolerable staleness, choose an eviction policy deliberately, watch the hit rate.

Four caching patterns

Four caching patterns, one read and one write
The pattern decides who talks to whom — and therefore who is responsible for keeping the cache honest.
1 get / 4 set3 on missAppCacheDatabase

The application talks to both.

1. app: GET cache[user:7]
2. cache: miss
3. app: SELECT … FROM users WHERE id = 7
4. app: SET cache[user:7] = row, TTL 300s
5. app: return row
Where staleness lives: Between the UPDATE and the DEL, or if the DEL fails, readers get the old value until the TTL expires.
Use when: The default. Simple, the cache can die without taking the app down, and only what is read gets cached.
Risk: Every call site must remember to invalidate. One forgotten DEL is a bug that looks like "the data is randomly wrong".

When to use — and when not

Use it when
  • A read far more frequent than the writes that would invalidate it, that tolerates bounded staleness.
Avoid it when
  • Data that must always be current with no bypass.
  • Writes as the bottleneck — a cache does not scale writes.
  • When a well-indexed query is already fast enough.

Failure modes

  • Forgotten invalidation on one write path.
  • noeviction on a full cache.
  • Caching data that changes as often as it is read, so the hit rate is low and staleness high.

See how this works internally →

Descend one layer: the same topic explained from the machinery up.

Don't delegate understanding
The manifesto →