Decorative background gradient
Back to Blog
CachingSystem DesignPerformance

Caching Strategies and Cache Invalidation — The Complete Guide

A practical guide to caching strategies (cache-aside, write-through, write-behind) and the cache invalidation techniques that keep them from serving stale data.

Caching Strategies and Cache Invalidation — The Complete Guide

Caching looks simple until it's in production: pick a strategy, wire it up, and reads get faster. The part that actually causes incidents isn't the caching — it's making sure the cache doesn't quietly serve wrong data after something changes underneath it. This guide covers the three main caching strategies and the invalidation techniques that keep each of them honest.

Strategy 1: Cache-Aside (Lazy Loading)

Cache-aside is the most common pattern because the application stays in control of both reads and writes.

ts

On a miss, the application reads from the database and populates the cache itself. This means the cache only ever holds data someone actually asked for — unpopular records never take up space. The tradeoff is that the first request after any cache miss (including after invalidation) pays the full database latency, and if you forget to invalidate on write, cache-aside has no built-in mechanism to catch that.

Strategy 2: Write-Through

Write-through updates the cache and the database as one logical operation, so a read right after a write never sees stale data.

ts

This eliminates the "stale read right after write" problem entirely, but every write now takes on the latency of a cache write in addition to the database write. It's a good fit for data that's read far more often than it's written — user profiles, product catalogs, configuration — and a poor fit for write-heavy workloads.

Strategy 3: Write-Behind (Write-Back)

Write-behind writes to the cache immediately and queues the database write to happen asynchronously afterward.

ts

This gives the best write latency of the three strategies, but it introduces real risk: if the queued write fails or the process crashes before it runs, the cache and database permanently disagree. Use write-behind only when you have a durable queue and a clear reconciliation story for failed writes — it's rarely worth the complexity outside of high-throughput analytics or metrics pipelines where losing a write occasionally is acceptable.

Invalidation Technique 1: Explicit Invalidation on Write

The most reliable invalidation approach is also the simplest: whenever code changes the source of truth, it deletes or updates the matching cache key in the same code path.

ts

The failure mode here isn't the technique — it's coverage. Every write path needs to invalidate every cache entry it could affect, including list views, aggregates, and search indexes that reference the changed record. Missed invalidation paths (background jobs, admin tools, bulk imports) are the actual source of most "why is this data stale" bugs, not a flaw in caching itself.

Invalidation Technique 2: TTL as a Safety Net

Even with careful explicit invalidation, always set a time-to-live on cached entries.

ts

A TTL bounds the damage of any invalidation path you missed — instead of a stale entry living forever, it self-corrects within the TTL window. Pick the TTL based on how tolerant the data is to staleness: a public blog listing might tolerate 10 minutes, while a user's own account balance should have a TTL of seconds, if any.

Invalidation Technique 3: Versioned Cache Keys

Instead of tracking down every individual key that needs deleting, bake a version number into the key itself and bump the version to invalidate everything at once.

ts

This turns "find and delete every related cache key" into "increment one counter" — old versioned keys become unreachable and expire on their own TTL instead of needing to be tracked down and deleted individually.

Invalidation Technique 4: Preventing Cache Stampedes

When a hot key expires, many concurrent requests can miss the cache at the same instant and all hit the database simultaneously, which can take down the database even though the cache was doing its job seconds earlier.

ts

Only the request that acquires the lock rebuilds the cache entry; everyone else waits briefly and retries against the now-populated cache instead of all hitting the database at once. For very high-traffic keys, probabilistic early expiration (refreshing the key slightly before its actual TTL, with a small random chance per request) avoids the stampede entirely by spreading the rebuild across time instead of concentrating it at the exact expiration moment.

Key Takeaways

Cache-aside is the right default for most read-heavy workloads, write-through removes stale-read-after-write risk at the cost of write latency, and write-behind should be reserved for cases where losing a write occasionally is tolerable. TTLs are a safety net that should exist even alongside explicit invalidation, versioned keys turn bulk invalidation into a single counter increment, and request coalescing or a short-lived lock is what stands between a hot cache key expiring and a cache stampede taking down your database.

Frequently Asked Questions

What is the difference between cache-aside and write-through caching?

In cache-aside, the application checks the cache first and falls back to the database on a miss, then populates the cache — writes go straight to the database and the cache is invalidated or updated separately. In write-through, every write goes to the cache and the database together as one operation, so the cache is never stale immediately after a write, at the cost of added write latency.

Why is cache invalidation considered hard?

Because the cache has no inherent knowledge of when the underlying data changes — every place that writes to the source of truth has to remember to also update or delete the relevant cache entries. Missing even one write path (a background job, an admin panel, a database migration) leaves a cache entry stale with no automatic way to detect it.

What is a cache stampede and how do you prevent it?

A cache stampede happens when a popular cache key expires and many concurrent requests all miss the cache at once, each independently querying the database to rebuild the same value — overwhelming it. Prevent it with request coalescing (only one request rebuilds the value while others wait), a lock on the cache key during rebuild, or probabilistic early expiration that refreshes the key before it fully expires.

Should I always set a TTL on cached data?

Yes, even when you also invalidate explicitly on writes. A TTL is a safety net for the write paths you forgot to invalidate — without one, a missed invalidation means that cache entry is wrong forever instead of self-correcting after a bounded window.

Working on something similar? Take a look at my services and case studies, or book a free call to talk about your idea.

Related Articles

Trending Topics