Caching Strategies (Write-Through, Write-Behind, Cache-Aside)
You've decided to add a cache. But how does the cache interact with your database? Who's responsible for loading data? What happens on writes? The answers to these questions define your caching strategy — and choosing wrong can cause data loss, stale reads, or worse performance than no cache at all.
The Four Strategies
1. Cache-Aside (Lazy Loading)
The application manages the cache explicitly. On reads, it checks the cache first. On misses, it loads from the database and populates the cache. On writes, it updates the database and invalidates (or updates) the cache.
Cache-Aside Flow
- App receives read request
- App checks cache — HIT? Return cached data
- MISS? App queries database
- App writes result to cache
- App returns data to caller
2. Write-Through
Every write goes to both cache AND database synchronously. The cache always has the latest data. Reads are fast (always a hit after the first write), but writes are slower because they must update two stores.
3. Write-Behind (Write-Back)
Writes go to the cache only. The cache asynchronously flushes changes to the database in the background. Writes are blazing fast, but you risk data loss if the cache crashes before flushing.
4. Read-Through
Similar to cache-aside, but the cache itself is responsible for loading from the database on a miss. The application only talks to the cache — it never directly queries the database.
Comparison Table
| Strategy | Best For | Consistency | Write Speed | Complexity |
|---|---|---|---|---|
| Cache-Aside | Read-heavy workloads | ⚠️ Eventual | Fast (DB only) | Medium |
| Write-Through | Read-heavy + consistency | ✅ Strong | Slow (2 writes) | Low |
| Write-Behind | Write-heavy workloads | ⚠️ Eventual | Fastest | High |
| Read-Through | Read-heavy, simple app | ⚠️ Eventual | N/A (read strategy) | Low (for app) |
When to Use Which
Decision Guide
- Cache-Aside — Default choice. Use when reads vastly outnumber writes, and you can tolerate brief staleness. Most web applications.
- Write-Through — Use when you need consistency and can afford slower writes. Financial data, inventory counts.
- Write-Behind — Use for write-heavy workloads where you can tolerate potential data loss. Analytics events, logging, activity feeds.
- Read-Through — Use when you want a simpler application layer, and your cache library supports it (e.g., Hibernate L2 cache, AWS DAX).
Warm-Up Strategies
A cold cache is a dangerous cache. After a deploy or a cache restart, every request is a miss and your database gets hammered. Two approaches:
Pre-Population vs Lazy Loading
- Pre-population (warming) — On startup, load the most commonly accessed data into the cache before serving traffic. Avoids the "cold start" stampede.
- Lazy loading — Let the cache fill naturally as requests come in. Simpler, but causes elevated latency and DB load initially.
- Hybrid — Pre-populate the top 1000 hot keys (you know from analytics), lazy-load the rest. Best of both worlds.
Real-World: Facebook's Look-Aside Caching
🏢 Facebook + Memcached
Facebook uses cache-aside (look-aside) with Memcached at massive scale to serve the social graph:
- Scale: Trillions of Memcached gets per day across thousands of servers.
- Pattern: Application checks Memcached → miss → query MySQL → write result to Memcached. Classic cache-aside.
- Why not write-through? Facebook's write patterns are complex (fan-out to friends' timelines). Cache-aside gives them full control over what gets cached and when.
- Invalidation: On writes, they delete the cache key (not update it). Next read triggers a fresh load. This avoids race conditions from concurrent writes.
- Lease mechanism: To prevent thundering herds on popular keys, Memcached issues a "lease" to the first client that misses — other clients wait or get stale data.
Interactive: Simulate Caching Strategies
Choose a strategy and run a sequence of operations. Watch how cache hits, misses, and DB load change: