Core Building Blocks

Caching Patterns: Cache-Aside, Read-Through, Write-Through, and More

Master essential caching patterns including cache-aside, read-through, write-through, write-behind, and refresh-ahead. Learn when to use each pattern and common pitfalls.

20 min readcachingcache-asideread-throughwrite-throughpatternssystem design

Why Caching Is Essential

Caching is one of the most effective ways to improve system performance and reduce load on backend services. By storing frequently accessed data in fast memory, we can dramatically reduce latency and increase throughput.

The 90/10 rule: In many systems, 90% of the reads go to 10% of the data. Caching that hot 10% can yield massive performance gains.

However, caching introduces complexity: cache invalidation, consistency challenges, and the infamous "cache stampede." Understanding caching patterns helps you apply caching correctly.


Cache-Aside (Lazy Loading)

The most common caching pattern where the application code is responsible for loading data into the cache.

How It Works

  1. Application looks for data in the cache
  2. If found (cache hit), return it immediately
  3. If not found (cache miss), load data from the database
  4. Store the loaded data in the cache for future requests
  5. Return the data to the caller

Code Example

python
def get_user(user_id):
    # Try cache first
    user = cache.get(f"user:{user_id}")
    if user is not None:
        return user
    
    # Cache miss - load from database
    user = database.query("SELECT * FROM users WHERE id = ?", user_id)
    
    # Populate cache for next time
    cache.set(f"user:{user_id}", user, ttl=300)  # 5 minute TTL
    return user

Pros

  • Simple to understand and implement
  • Cache only contains data that's actually requested
  • Natural integration with existing code

Cons

  • Cache miss penalty includes both cache lookup and database query
  • Application code must handle both cache and database logic
  • Potential for cache stampede (many processes querying DB simultaneously on miss)
💡

Mitigating stampede: Use techniques like locking, probabilistic early expiration, or request coalescing to prevent thousands of requests from hitting the database when a popular key expires.


Read-Through

In this pattern, the cache itself is responsible for loading data from the data store on a cache miss.

How It Works

  1. Application requests data from the cache
  2. If found (cache hit), return it immediately
  3. If not found (cache miss), the cache loads data from the database
  4. Cache returns the data to the application and stores it for future requests

Key Difference

With read-through, the application code only interacts with the cache. The cache provider handles the database interaction behind the scenes.

Pros

  • Application code is simpler (only deals with cache)
  • Centralized loading logic in the cache layer
  • Reduces cache stampede risk (loading happens in cache layer)

Cons

  • Requires a cache provider that supports read-through (Redis modules, custom implementations)
  • Less control over loading logic for the application
  • Still has cache miss penalty

Write-Through

Data is written to both the cache and the database synchronously.

How It Works

  1. Application writes data to the cache
  2. Cache synchronously writes data to the database
  3. Cache returns success to the application only after both writes succeed

Pros

  • Strong consistency between cache and database
  • Cache is always fresh (no stale reads for recently written data)
  • Simple mental model: cache is always up-to-date

Cons

  • Write latency includes both cache and database write times
  • Higher write load on the database (every write goes to both)
  • Can reduce write throughput significantly
⚠️

Write amplification: Every write results in two writes (cache + DB). This can overwhelm your database if write-heavy.


Write-Behind (Write-Back)

Data is written to the cache immediately and asynchronously written to the database later.

How It Works

  1. Application writes data to the cache
  2. Cache returns success immediately
  3. Cache asynchronously writes data to the database in the background
  4. Application continues without waiting for database write

Pros

  • Low write latency (only cache write time)
  • Can batch multiple writes to the database for efficiency
  • Absorbs write spikes (cache acts as a buffer)

Cons

  • Risk of data loss if cache fails before writing to database
  • Eventual consistency between cache and database
  • More complex failure handling and recovery logic

Use case: Write-behind is great for workloads with high write tolerance for brief inconsistency, like analytics counters or social media likes.


Refresh-Ahead

Proactively refresh cache entries before they expire to avoid cache misses.

How It Works

  1. Cache tracks access patterns and predicts when entries will expire
  2. Before expiration, cache asynchronously loads fresh data from the database
  3. When expiration hits, cache serves the freshly loaded data (no miss)
  4. Application never experiences a cache miss for predictable access patterns

Pros

  • Eliminates cache miss penalty for predictable traffic
  • Maintains low latency even under load
  • Reduces database load spikes

Cons

  • Requires cache with intelligent prediction capabilities
  • May waste resources refreshing data that won't be accessed
  • More complex to implement correctly

Cache Invalidation Strategies

No discussion of caching is complete without addressing the hardest problem in computer science: cache invalidation.

Time-Based Expiration (TTL)

  • Simplest approach: set a time-to-live on each cache entry
  • Pros: Simple, predictable memory usage
  • Cons: Can serve stale data until expiration; doesn't reflect data changes

Write-Based Invalidation

  • Invalidate cache entries when data is updated in the database
  • Pros: Keeps cache fresh; eliminates stale reads after writes
  • Cons: Requires coordination between database writes and cache deletes

Pros

  • Cache stays consistent with database
  • Eliminates stale read window after writes

Cons

  • More complex to implement (need to intercept database writes)
  • Can cause cache stampede if many related entries are invalidated simultaneously
💡

Pattern: Combine TTL as a safety net with write-based invalidation for freshness.


Choosing the Right Pattern

PatternBest ForConsistencyComplexity
Cache-AsideRead-heavy workloads, simple appsEventualLow
Read-ThroughApps wanting simple cache interfaceEventualMedium
Write-ThroughStrong consistency requirementsStrongMedium
Write-BehindWrite-heavy, tolerant of brief inconsistencyEventualHigh
Refresh-AheadPredictable access patterns, latency-sensitiveEventualHigh
🚨

Common pitfalls:

  • Caching everything (wastes memory, increases complexity)
  • Ignoring cache invalidation (leads to stale data)
  • Not handling cache stampede (overwhelms backend on miss)
  • Overlooking memory eviction policies (unexpected cache misses)

What to Remember for Interviews

  1. Know the patterns: Be able to explain cache-aside, read-through, write-through, write-behind, and refresh-ahead.
  2. Trade-offs: Understand the consistency, complexity, and performance implications of each.
  3. Invalidation: Know why cache invalidation is hard and strategies to handle it.
  4. Stampede mitigation: Be familiar with techniques to prevent cache stampede.
  5. Real-world systems: Know how popular caches (Redis, Memcached) implement these patterns.

Practice: Design a caching layer for a social media feed. Which pattern would you use for user profiles? For the feed itself? For counting likes? Justify your choices.