Caching strategies: cache-aside, write-through, write-back

Cache-aside, read-through, write-through and write-back compared: how each works, how each fails, and which suits course pages, progress and leaderboards.

8 min read
On this page 8 sections
  1. Cache-aside (lazy loading)
  2. Read-through
  3. Write-through
  4. Write-back (write-behind)
  5. Consistency and failure modes
  6. Which pattern for which data
  7. Key takeaways
  8. Frequently asked questions

The four common caching strategies differ in who fills the cache and when writes reach the database. In cache-aside (lazy loading), the application checks the cache and fills it on a miss; read-through moves that loading into the cache layer; write-through updates the cache and the database together on every write; and write-back, also called write-behind, writes to the cache first and flushes to the database later. Most platforms use cache-aside by default and reach for the others only for specific kinds of data.

Cache-aside (lazy loading)

Cache-aside is the pattern behind most Redis and Memcached code, and the one Microsoft's architecture guide describes as the Cache-Aside pattern. The application talks to both the cache and the database:

def get_course(course_id):
    key = f"course:{course_id}"
    course = cache.get(key)
    if course is None:                      # miss: load and remember
        course = db.fetch_course(course_id)
        cache.set(key, course, timeout=600)
    return course

def update_course(course_id, changes):
    db.update_course(course_id, changes)    # 1. write the database
    cache.delete(f"course:{course_id}")     # 2. then invalidate the copy

Notice that the write path deletes the cached copy rather than updating it. Facebook's paper on scaling memcache explains why it chose the same: deletes are idempotent, so repeating one or running two in the wrong order does no harm, whereas two updates that arrive out of order leave the older value in the cache.

Cache-aside's strengths are that only data someone actually reads gets cached, and that the application keeps working, just slower, if the cache goes down. Its weaknesses are a slow first read after every miss, a window of staleness, and one race worth knowing by heart:

StepRequest A (reads)Request B (writes)Cache now holds
1Misses, reads the fee ₹4,999 from the databaseNothing
2Updates the fee to ₹3,999Nothing
3Deletes the cache keyNothing
4Stores ₹4,999, the value it read in step 1₹4,999, which is stale

The stale fee now stays until the entry expires. That is why every cache-aside entry needs a TTL as a safety net, however good your invalidation. Stronger fixes exist: Facebook's leases let the cache refuse a write from a reader whose lease was cancelled by a delete in between, and you can store a version or updated_at with each value and refuse to overwrite a newer one. Some teams also repeat the delete a second or so after the write.

Read-through

In read-through caching, the application only ever asks the cache. On a miss, the cache itself, or a library wrapped around it, calls a loader you registered, stores the result and returns it. The behaviour is the same as cache-aside; what changes is where the code lives. Loading logic sits in one place instead of being repeated around every call, which makes it easier to add locking or metrics later.

Redis doesn't load from your database by itself, so with Redis, read-through usually means a small wrapper in your code. Django's cache.get_or_set(key, loader, timeout) is a one-line version of the idea. The same race and the same need for TTLs apply, because the underlying sequence of reads and writes hasn't changed.

Write-through

Write-through updates the cache as part of every write. The application writes the database, and on success writes the new value into the cache straight away, so the next reader finds fresh data instead of a miss. Microsoft's guide makes the contrast explicit: with cache-aside a reader can briefly see a miss or stale data after a write, while write-through lets readers see the new value immediately.

The costs are real. Every write pays for two systems, the cache fills with data nobody may read (so keep a TTL), and a failure between the two writes leaves the cache behind the database. Two concurrent writers can also land their cache writes in the opposite order to their database writes, so compare versions before overwriting. Write-through suits data that is read right after it is written, such as a student's profile settings or a course's published flag, where a stale read would confuse the person who just made the change.

Write-back (write-behind)

Write-back reverses the order: the application writes to the cache, gets an acknowledgement, and a background job flushes changes to the database later, often in batches. It absorbs write bursts and merges repeated updates to the same key into one database write.

Video progress is the textbook case. Take an illustrative 30,000 students watching recorded lectures, with the player reporting each student's position every 15 seconds. Writing every report to the database means 2,000 writes a second. Keeping the latest position per student in Redis and flushing once a minute means 500 row updates a second, and batching them into multi-row statements cuts the number of database round trips much further. If Redis loses a few seconds of positions, a student resumes a few seconds early, which nobody minds.

The risk is exactly that loss. Anything not yet flushed disappears if the cache node dies, and even Redis's append-only file with its default once-a-second fsync can lose about a second of writes. Meanwhile other systems reading the database see old data. Never use write-back for payments, test submissions or marks. If you do use it, drive the flush from a durable queue or stream so a crash can resume where it stopped; message queues and background jobs covers the machinery.

Consistency and failure modes

StrategyRead right after your own writeIf the cache goes downMain risk
Cache-asideA miss, then fresh data, provided the delete succeededReads fall back to the database: slower but correctStale sets from races; stampedes when hot keys expire
Read-throughSame as cache-asideDepends on the wrapper; add a database fallbackSame as cache-aside, with loading hidden from view
Write-throughFreshWrites must skip the cache or fail; reads fall backSlower writes; cache and database diverging after a partial failure
Write-backFresh, from the cacheUnflushed writes are lostData loss; the database lags behind the truth

A fifth pattern, refresh-ahead, rebuilds popular entries shortly before they expire so readers never wait for a miss. It is mainly a defence against the cache stampede, where a hot key expires and many requests rebuild it at once.

Which pattern for which data

DataPatternWhy
Course catalogue and syllabusCache-aside, delete on publish, TTL of 10 to 60 minutesRead constantly, changed rarely; a short stale window is harmless
Test paper for a scheduled mock testWrite it into the cache before the test opens, then read-throughDon't let 20,000 students discover the miss at 10 a.m.
Video watch positionWrite-backFrequent, merge-able updates where losing seconds is fine
Lesson or course completionDatabase first, then write-through or deleteMust never be lost; the student expects to see it at once
Live leaderboard during a testRedis sorted set updated on each submission, rebuilt from the database if lostSorted sets rank natively; the database stays the record
Leaderboard after the testA materialized view or a cache-aside entry with a 30 to 120 second TTLEveryone reads the same ranking
Fees, payments and order statusNo cache, or cache-aside with a TTL of secondsCorrectness beats speed; read from the primary right after a write
Live-class viewer countsWrite-back countersApproximate numbers are fine

Whatever the pattern, invalidation decides whether it's correct in practice; see cache invalidation for TTL choices and versioned keys, and Redis caching for eviction and memory. For where this layer sits among browser, CDN and database caches, see types of caching.

Key takeaways

  • Cache-aside is the default: read the cache, load on a miss, and delete (don't update) on write.

  • Read-through is cache-aside with the loading moved into one wrapper; it has the same races.

  • Write-through gives fresh reads after writes at the cost of slower writes and partial-failure handling.

  • Write-back absorbs write bursts but can lose unflushed data, so keep it to counters and progress pings.

  • Give every entry a TTL as a safety net, whichever pattern fills it.

Frequently asked questions

What is cache aside pattern?

Cache-aside is a caching pattern in which the application manages the cache itself. To read, it checks the cache first; on a miss it loads the data from the database, stores a copy in the cache with a TTL and returns it. To write, it updates the database and then deletes the cached copy, so the next read loads the new value. It is the most common way to use Redis or Memcached.

What is cache aside strategy?

As a strategy, cache-aside means caching lazily and invalidating on write: nothing enters the cache until someone reads it, and every change removes the stale copy. Choose it for read-heavy data that changes occasionally, such as course pages. Pair it with a TTL to cap how long a missed invalidation or race can leave stale data, and with a lock or early refresh for very hot keys.

What is look aside cache?

Look-aside cache is another name for cache-aside: the cache sits beside the database rather than in front of it, and the application looks in the cache before going to the database itself. Facebook's memcache paper calls its setup a demand-filled look-aside cache. The opposite arrangement is an inline cache, as in read-through and write-through, where the application talks only to the cache.

Share this article

Looking for something else?

Talk to Us