Stage 1 of 9 · Model
How a look-aside cache behaves
Reads: get from the cache; on a miss, query MySQL and set the result. Writes: update MySQL, then do something about the cached copy.
What you need to know first
A look-aside (or cache-aside) cache sits next to the database, not in front of it. The application does the work:
- Read: ask the cache. On a hit, done. On a miss, query the database and put the result in the cache.
- Write: update the database, then deal with the cached copy.
The cache never talks to the database itself. It's a disposable copy the application manages. See Caching.
The cache serves 1,000,000 reads a second with a 99% hit rate. How many reads a second reach the database?
About 10,000 per second.
1% of 1,000,000 = 10,000 a second. The database is provisioned for this miss load, not the total.
The hit rate drops from 99% to 98%. What happens to database load?
It roughly doubles: misses go from 1% to 2%.
Database load follows the miss rate, not the hit rate. A one-point drop in hits is a 100% increase in misses.
On write, there are two options for the cached copy: set the new value, or delete the key and let the next read refill it.
Deletes are idempotent (doing it twice is the same as once) and order-insensitive (two deletes in either order leave the same result). Two sets racing can arrive in the wrong order and leave the older value cached.
What the stage asks
Which statements hold?
- Fails
After a write, the web server should set the new value in the cache so the next read hits.
Facebook deletes instead. A delete is idempotent and order-insensitive: two deletes in any order leave the same result. Two concurrent sets can arrive in the wrong order and leave the older value cached. The next read refills from the database.
- Holds
Because the cache is not the source of truth, losing any cached key must always be safe.
It may cost a database query, but never correctness. Hold on to this: stage 7 introduces a key for which it is no longer true.
- Fails
To keep latency low, a page that needs 500 keys should fetch them one by one.
500 sequential round trips is far too slow. Clients batch independent keys into parallel multigets, ordering them by data dependencies. That creates its own problem: hundreds of responses arriving at once (incast), which clients limit with a sliding window of outstanding requests.
- Holds
If the hit rate drops from 99% to 98%, the database receives about twice as many reads.
Misses go from 1% to 2% of reads. Small changes in hit rate are large changes in database load, which is why everything later in this investigation is about protecting the miss path.
The reasoning
- In a look-aside cache, the database is the truth, the cache is a disposable copy, and writes delete.
- Database load equals the miss rate, so small hit-rate drops are large database load increases.
- Deletes are idempotent and order-insensitive; racing sets can leave old values cached.
The look-aside contract is simple: the database is the truth, the cache is a disposable copy, writes delete. The arithmetic is the part people miss: at high hit rates, the database's load is the miss rate, so anything that causes a burst of misses (a hot key, a dead server, an empty cluster) is a database incident.