Caching Strategies
Caching stores frequently accessed data in faster storage. The strategy you choose — cache-aside, write-through, write-behind, or refresh-ahead — determines when data is written to the cache, how stale it can be, and what happens during failures. Choosing the wrong strategy causes stale data, cache stampedes, or data loss.
How it works
A cache is a faster, smaller storage layer that sits between the application and the slower, larger source of truth (usually a database). The application checks the cache first; on a hit, it returns the cached value (fast). On a miss, it fetches from the database (slow) and writes the result to the cache for next time.
The caching strategy determines when and how data is written to the cache. There are four main strategies, each with different trade-offs in freshness, write latency, and failure behavior.
Cache Aside (Lazy Loading) — the most common strategy.
The application manages the cache explicitly:
- Check cache for the key.
- If hit → return the cached value.
- If miss → fetch from DB, write to cache with TTL, return.
Writes update the database, then invalidate (delete) the cache entry. The next read will miss and re-populate from the DB.
Pros:
- Cache only holds data that's actually read (no wasted memory).
- Fault-tolerant: if the cache crashes, the system still works (just slower).
- Simple to implement.
Cons:
- Stale data: between a write and the cache invalidation, reads can return old data. TTLs cap the staleness window.
- Cache stampede: when a popular key expires, N concurrent requests all miss and all fetch from the DB simultaneously.
- Application code is aware of the cache (not transparent).
When a popular cache entry expires, the next N requests all see a miss at the same time. They all fetch from the DB, all write to the cache. The DB takes N× the expected load for a brief window. Mitigations: (1) cache locking — only one request fetches, others wait; (2) early refresh — refresh before expiry; (3) probabilistic early expiration — add jitter to TTLs so they don't all expire at once.
Write Through — the cache is always fresh.
Writes go to both the cache AND the database synchronously. The application writes to the cache; the cache writes to the DB and returns only after both succeed. Reads always hit the cache.
Pros:
- Cache is always fresh — no stale reads.
- Read performance is optimal (always cache hit, assuming data has been written).
- Simple read path (no DB fallback).
Cons:
- Higher write latency (must write to cache + DB synchronously).
- If the cache is down, writes fail (unless you add fallback logic).
- Cache holds everything ever written, even if never read (wasted memory).
Best for: data that's written once and read many times, where staleness is unacceptable.
Write Behind (Write-Back) — the fastest writes, but risky.
Writes go to the cache only. The cache returns immediately. A background process writes to the DB asynchronously (batched, delayed).
Pros:
- Extremely fast writes (only one write to cache, no DB round-trip).
- DB load is smoothed (batched writes).
- Survives brief DB outages (writes queue in cache).
Cons:
- Data loss risk: if the cache crashes before flushing to DB, committed writes are lost.
- Reads from the DB (by other services, analytics) can see stale data.
- Complex to implement (write queue, flush logic, ordering).
Best for: high-write, low-criticality data (counters, analytics events, telemetry). Never use for payments or transactions.
Refresh Ahead — popular items never expire.
The cache proactively refreshes entries before their TTL expires. A background process watches for entries nearing expiry; if they're frequently accessed, it refreshes them from the DB before they expire.
Pros:
- Popular items never miss — the user never waits for a DB fetch.
- Smooths DB load (refreshes are spread over time, not bursty).
Cons:
- Wastes resources refreshing items that nobody reads.
- Complex to implement (need to track access frequency).
- If the refresh fails, the item expires normally.
Best for: systems with clear 'hot' items (home page content, popular product pages, trending topics).
Your cache aside system uses a 5-minute TTL on user profiles. A user updates their bio, the DB write succeeds, but the cache invalidation call fails (cache is briefly down). What happens?
Pick one answer.
You need to cache analytics events that are written at 100,000 events/sec but only read for dashboards (rarely). Which strategy is best?
Pick one answer.
A popular product page's cache entry expires. Within the same second, 1000 users request the page. What happens, and how do you mitigate it?
Pick one answer.
Cache hierarchy — layered caches compose. Production systems rarely have one cache. They have a stack: each layer is faster but smaller, and each serves misses from the layer below.
A typical web app stack:
- Browser cache (per-user, seconds-to-days TTL). Static assets with hashes in filenames (e.g.,
app.abc123.js) cached for a year. HTML cached for seconds. - CDN edge cache (per-PoP, minutes-to-hours TTL). Static assets, sometimes HTML, cached at 300+ PoPs globally. ~10ms latency to user.
- App-server in-process cache (per-instance, seconds TTL). Frequently accessed config, computed values. ~0.01ms latency. Lost on restart.
- Distributed cache (Redis/Memcached) (shared, seconds-to-minutes TTL). User profiles, session data, query results. ~1ms latency. Survives app restarts.
- Database query cache (per-DB, often disabled). Cached query results. Often more trouble than it's worth (invalidation is hard).
- Database buffer pool (per-DB, transparent). Pages cached in RAM. ~0.1ms latency. The OS page cache provides another layer.
The key insight: each layer only holds a fraction of the data, and each layer serves ~90% of its requests from cache. So 1000 user requests might result in 100 CDN misses (10%), of which 10 reach the app (90% served from CDN), of which 1 reaches the DB (90% served from Redis). The DB sees 0.1% of original load. This is why a small cache can carry a 1000x-larger database.
All four strategies, side by side.
| Strategy | When written to cache | Read path | Write latency | Staleness | Failure risk | Best for |
|---|---|---|---|---|---|---|
| Cache-aside | On read miss (lazy) | cache → DB on miss | low (DB only) | up to TTL | cache crash = slow, not broken | read-heavy, can tolerate staleness |
| Write-through | On write (sync to both) | cache always hits | high (cache + DB sync) | none | cache down = write fails | writes-once-read-many, freshness critical |
| Write-behind | On write (cache only, DB async) | cache always hits | very low (cache only) | DB may lag seconds-minutes | cache crash = data loss | high-write, low-criticality (counters, telemetry) |
| Refresh-ahead | Proactively before TTL expires | cache always hits (for popular items) | low | none (for popular items) | wasted work on cold items | hot items (home page, trending) |
The right strategy depends on (1) read:write ratio, (2) staleness tolerance, (3) write latency tolerance, and (4) data-loss tolerance. Most systems combine: cache-aside as the default, write-through for transactional state, write-behind for counters and analytics, refresh-ahead for hot keys.
The architect's rule: start with cache-aside. Add complexity only when measurements prove you need it. Most cache-related outages come from over-engineering (write-behind on data you can't afford to lose) or under-engineering (cache-aside on a hot key without stampede protection).
Netflix's EVCache is a Memcached-based distributed cache deployed across multiple AWS regions. It serves 90%+ of Netflix's read traffic — user profiles, watch history, content metadata, recommendations. The architecture: each region has its own EVCache cluster (multiple shards for horizontal scaling, with replicas for HA). Writes go to the local region's EVCache synchronously (write-through for user state, cache-aside for content metadata), then replicate cross-region via EVCache's replication layer (asynchronous, ~seconds latency). On a region failure, Netflix routes traffic to a healthy region via Route 53 — the cache there may be slightly stale but the system stays available. This is a textbook example of layered caching (CDN → app → EVCache → Cassandra) and per-workload strategy choice (write-through for user state, cache-aside for content). The key insight: Netflix didn't pick one caching strategy — they use multiple, tuned per workload.
Your team lead proposes using write-behind caching for the payment system: 'Writes are 100x faster, the DB catches up in a few seconds, what could go wrong?' What's the right response?
Pick one answer.
The carousel requires joining 5 tables to compute (DB query: ~500ms). When the cache hits, response is 2ms. When it misses, response is 502ms. At the 5-minute TTL boundary, all 10K req/sec suddenly see misses.
Your e-commerce site caches the home page product carousel in Redis with a 5-minute TTL. At peak (Black Friday), the carousel is requested 10,000 times/sec. Every 5 minutes, your database CPU spikes to 100% for ~10 seconds. Diagnose and fix.
Engineering mental model
Mental model. Think of Caching Strategies as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Caching Strategies mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing Caching Strategies, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.
// Pseudocode
request = receive()
result = caching_strategies(request)
return result
// Production questions:
// 1. What happens on timeout?
// 2. Can this operation be retried safely?
// 3. What is the bottleneck?Back-of-the-envelope reasoning
Example: 10M requests/day ÷ 86,400 ≈ 116 requests/s average. Design for peak rather than average; a 10× peak is ≈ 1,160 requests/s.
Interactive thought experiment: Caching Strategies
Change the variables below and predict what breaks first in Caching Strategies. The production lab can later reuse these same inputs.
Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.
If you are stuck on Caching Strategies, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.
You increase traffic by 10× in a system using Caching Strategies. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using Caching Strategies?
Pick one answer.
You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.
Production scenario: your system uses Caching Strategies, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose Caching Strategies, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.
For Caching Strategies, reason in this order: what data is hottest, where can it live closer to the caller, what makes it stale, and what happens on a miss or cache failure. A cache is an optimization boundary, not the source of truth.
Numerical sanity check
A useful first-order model is cache_load = request_rate × (1 - hit_rate). If traffic is 20,000 req/s and the hit rate is 90%, roughly 2,000 req/s still reaches the origin before considering misses caused by expiration or eviction.
Do not optimize for a memorized definition. Reason from the workload and failure mode.
You have a read-heavy Caching Strategies path. Traffic doubles overnight. What metric would you inspect first, and what would convince you to add another cache layer?
Pick one answer.
What you gain, what you pay
- +Dramatically reduces latency — cache hits are 100x faster than DB reads.
- +Reduces database load — hot data served from cache, DB handles cold reads only.
- +Multiple strategies for different use cases (freshness vs speed vs complexity).
- −Stale data — every strategy has some window where cache and DB diverge.
- −Cache stampede — popular key expiry can overwhelm the DB.
- −Operational complexity — monitoring cache hit rate, eviction policy, memory usage.
- −Data loss risk (write-behind) — if cache crashes before flushing to DB.
How this breaks in production
- Stale data window — cache and DB diverge between write and invalidation.
- Cache stampede — popular key expiry causes thundering herd.
- Cache thrashing — keys evicted before they're read again (cache too small for working set).
- Data loss (write-behind) — cache crash before DB flush.
Don't fall into these traps
- •No TTL. Without a TTL, a cache invalidation failure means stale data forever.
- •Caching everything. Caching rarely-read data wastes memory and adds invalidation complexity.
- •Forgetting cache stampede protection on hot keys.
- •Updating cache before DB. If the cache write succeeds but the DB write fails, you are now inconsistent. Always write DB first, then cache.
Real systems using this
How real systems implement this
- Netflix EVCache — Multi-tier caching: CDN edge → origin cache (EVCache, built on Memcached) → database. 90%+ of reads served from cache. Cache-aside for content metadata, write-through for user state.
- Redis — In-memory key-value store used as a cache. Supports cache-aside (GET/SET/DEL), write-behind (with background sync to DB), and TTLs. Most popular cache in production.
Practice saying it out loud
- Q1Compare cache-aside, write-through, write-behind, and refresh-ahead.
- Q2What is a cache stampede, and how do you prevent it?
- Q3Your cache and database diverged. How do you detect it, and how do you fix it?
- Q4When would you NOT use caching? What are the risks?
Further reading & references
Core explanations are original NO CAP material. External references are provided for deeper study and standards.
What next?
Mark as understood once the mental model clicks.
Next recommended
Cache Aside (Lazy Loading)