Caching Strategies: Cache-aside, Write-through, Write-back
easyA cache trades correctness guarantees for speed by keeping a hot subset of data in fast storage close to the consumer. Caches live at every layer (CPU, OS page, application, distributed, CDN); the interview cache is usually a distributed cache (Redis, Memcached) between a service and its database.
Key Concepts
SET NX), probabilistic early refresh, or queued fill. Hot key: one key saturates one node — replicate hot key across N nodes, add client-side micro-cache, or shard the hot counter. Cache penetration: requests for keys that don't exist — add negative caching or a Bloom filter front. Cold cache after restart hammers the DB — warm via shadow traffic or persistent Redis.Approach
- Place the cache. Application sidecar (e.g. local Caffeine) gives sub-µs latency for tiny hot sets. Distributed cache (Redis cluster, Memcached) for shared state.
- Choose the strategy. Default: cache-aside with TTL. Use write-through for safety on critical data; write-back for high-throughput counters; refresh-ahead for predictable hot keys.
- Set TTLs deliberately. Mix base TTL (1-15 min for most data) + random jitter (10-20%) to spread expiry.
- Solve stampede. Single-flight via mutex (Redis SET NX) so only one filler hits the DB. Or probabilistic early refresh: refresh with probability that increases as TTL approaches.
- Solve hot keys. Detect via Top-K (Redis-cell module, sketches). Mitigate via replication across cache nodes, in-process client cache, or sharded counters.
- Decide invalidation. TTL-only is simplest and resilient. Add pub/sub purge for tight freshness windows. Versioned keys (cache key includes hash of underlying record) eliminate explicit invalidation.
- Plan for cache loss. Restart cold-cache fills overwhelm the DB. Use rolling restart, shadow traffic to warm, or capacity buffer.
Strategies in depth
Cache-aside: app reads cache → miss → read DB → populate. Write: write DB → invalidate. Simplest, most flexible. Two writes race possible (TOCTOU).
Read-through: cache library transparently fetches on miss. Cleaner code; same semantics as cache-aside.
Write-through: write to cache and DB synchronously. Cache never stale relative to DB. Doubles write latency.
Write-back: write to cache → flush to DB async. Fast writes but data loss if cache crashes before flush. Used for high-throughput counters with WAL backup.
Refresh-ahead: predictively reload hot entries before TTL expires. Good for predictable hot keys; wasteful for long-tail.
Components
- Cache cluster — Redis (rich data types, persistence, pub/sub) or Memcached (simpler, faster, eviction-only).
- Sharding — consistent hashing with virtual nodes, client-side or via proxy (twemproxy, mcrouter, KeyDB).
- Eviction — LRU (default), LFU, TinyLFU (better hit rate at small sizes), allkeys-LRU vs volatile-LRU.
- Invalidation channel — Redis pub/sub or Kafka topic for cross-region purges.
- Stampede control — single-flight mutex, probabilistic refresh, or queue-based fill.
- Hot-key handling — Top-K detector, replicated copies, client-side micro-cache.
- Metrics — hit rate, p99 latency, evictions, memory, hot-key alarms, slow-log.
Trade-offs
Cache-aside: flexible, but two-write race means brief inconsistency.
Write-through: strong consistency, slower writes.
Write-back: fastest writes, can lose data on cache failure.
TTL: short = fresh + DB hot; long = stale risk + cache effective. Jitter mandatory at scale.
Explicit invalidation: precise but requires reliable pub/sub. Pubsub messages can drop — combine with TTL as backstop.
Local + distributed cache: 2-layer cache (sidecar L1 + Redis L2) cuts latency further but increases invalidation complexity.
Failure modes and fixes
Thundering herd: N requests miss simultaneously, all hit DB. Fix: single-flight mutex (SET NX), probabilistic early refresh, or queued fill.
Hot key: one key gets all traffic, saturates one node. Fix: replicate key across N nodes, client-side micro-cache, or split hot counter into shards summed periodically.
Cache penetration: requests for keys that don't exist. Fix: negative caching (cache the 'not found' result with short TTL), Bloom filter front of cache.
Cache avalanche: many keys expire at once due to synchronized TTL. Fix: TTL jitter.
Cold cache after restart: DB hammered as cache refills. Fix: warm via shadow traffic, persistent Redis (AOF), or staged restart.
Real-world patterns
- Meta TAO: write-through cache layered on MySQL; serves billions of social-graph reads/s.
- Twitter Twemcache: heavily tuned Memcached fork; segmented LRU, fine eviction control.
- Netflix EVCache: Memcached-compatible cluster across AZs with cross-region replication; CDN-warmth for cold-start avoidance.
- Stripe: aggressive caching of idempotency keys and rate-limit counters in Redis Cluster.
- Pinterest: multi-tier cache; Memcached L1 + sharded Redis L2 for pinboards.