Caching Strategies (Write-Through, Write-Behind, Cache-Aside)

📘 Chapter 5: Caching ⏱️ 9 min read 🏗️ Lesson 020

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

  1. App receives read request
  2. App checks cache — HIT? Return cached data
  3. MISS? App queries database
  4. App writes result to cache
  5. 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.

Cache-Aside (Lazy Loading) App Cache DB 1. check 2. miss→query 3. populate App manages both cache and DB ✅ Simple, flexible ❌ Cache miss = slow first request Write-Through App Cache DB write write simultaneous Every write → both cache + DB ✅ Cache always consistent ❌ Higher write latency Write-Behind (Write-Back) App Cache DB 1. write 2. async flush Write to cache, async persist to DB ✅ Fastest writes ❌ Risk of data loss on crash Read-Through App Cache DB 1. read 2. miss→load Cache itself fetches from DB on miss ✅ App logic simpler ❌ Cache must know how to query DB
Figure 1: The four caching strategies — each optimizes for different read/write patterns.

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:

0
Cache Hits
0
Cache Misses
0
DB Operations
Cache contents: (empty)