How it works
The explanation, with examples you can run.
Cache-aside
The application owns the logic. Read from the cache; on a miss, load from the database, store with a TTL, return.
GET product:7 # miss: (nil)# ...application queries the database...SET product:7 '{"id":7,"name":"Kettle","price":2900}' EX 300GET product:7 # hit for the next five minutesOnly data that is actually read gets cached, and if Redis is down the application can still fall back to the database (slower). The cost is that the first read after a miss is slow, and cached data can be stale for up to the TTL.
Write-through and write-behind
- Write-through: every write updates the database and the cache together, so reads are always fresh. Everything written is cached, read or not.
- Write-behind: write to the cache, and copy to the database later. Fast writes, but anything not yet copied is lost if the cache fails. Rarely worth it for data you cannot lose.
Invalidation
When the source changes, the simplest robust rule is: write the database, then delete the key.
DEL product:7Setting the new value from the writer instead invites races: two writers can update the cache in the opposite order to their database commits, leaving the older value cached until the TTL ends. Deleting is not perfect either (a reader that loaded the old row just before the write can store it after the delete), which is why a TTL is the safety net in every pattern.
Stampedes
When a hot key expires, every request that arrives before it is reloaded misses, and each one runs the expensive query. This simulation sends 200 concurrent requests for a just-expired key:
Press Run to see what your code prints, in order.
Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.
Single flight works within one process. Across many application instances, the usual tools are:
- A short lock in Redis: the first instance to
SET lock:product:7 <token> NX PX 5000reloads the value; the others briefly serve the stale copy or retry. - Early refresh: reload a popular key before it expires (for example, when its remaining TTL drops below some fraction, with a bit of randomness so instances do not all refresh together).
- Jittered TTLs, so keys written together do not expire together.
- Stale-while-revalidate: keep the old value a little longer and serve it while one request refreshes it.
What to cache
Cache what is read far more often than it changes, and size the TTL by how stale it may be. Track the hit ratio (keyspace_hits and keyspace_misses in INFO stats): a cache with a low hit ratio adds a network hop to every request and saves little.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.