Skip to content

Caching

Keep a copy of data close and fast so most requests skip the slow source. Covers cache-aside, write-through, write-behind, eviction and CDN caching.

beginner30 minAsked oftenCachingCache asideWrite throughEvictionCdnRedis

Last reviewed 24 Oct 2026 · by TheLearningLoop Team

Progress is saved on this device. Create a free account to keep it everywhere.

On this page ▾

Key takeaways

  • A cache keeps a copy of data in a faster place, so a hit is fast and the slow source is read less.
  • A cache is small, so it needs an eviction rule such as LRU, and an expiry (TTL) or deletion on write so it does not serve stale data.
  • Never rely on the cache for correctness. If it empties, the source must be able to cope.

Why you need a cache, and when to use it

Reading from a database or another service is slow compared with reading from memory. If thousands of users ask for the same thing, the slow source repeats the same work again and again. A cache keeps a copy of data in a faster place, so most requests are answered without touching the source.

It gives you two things interviewers always ask about:

  • Lower latency: a hit is answered in a moment instead of waiting for the database.
  • Less load on the source: every hit is one read the database never sees, so it can serve more users or be smaller.

Use a cache when:

  • Data is read far more often than it changes: product pages, profiles, configuration.
  • The same data is asked for repeatedly (a few keys are popular).
  • A result is expensive to compute and can be reused.
  • A little staleness is acceptable, for example a view count that is a minute old.

Skip it, or use something else, when:

  • Every request asks for different data (no repeats): a cache will mostly miss. The demo's "scanning" traffic shows this.
  • Data must always be exactly current, such as an account balance at the moment of payment: read the source.
  • The query is just slow: add an index or fix the query first (see the Databases lesson).
  • The data is large and rarely read: caching it wastes memory.

Try it

Pick an eviction rule and a kind of traffic, then step through the 20 requests. The cache holds only four items. Watch the green and red dots, and the hit rate.

Cache hits, misses and eviction

Step 1 of 21 · Press Space to play, arrow keys to step
When full, evict:
Requests:
Request
-
  • empty
  • empty
  • empty
  • empty
Database
slow

Green dot = hit, red dot = miss

Start: an empty cache with room for 4 items, in front of a database. A hit is answered from the cache in about 2 ms. A miss goes to the database and takes about 50 ms. Those times are illustrative.

requests:
0
hits:
0
misses:
0
hit rate %:
0
database reads:
0
average latency (ms):
0

1 request = one step. Latencies are illustrative: a hit is about 2 ms and a miss about 50 ms.

Things to try:

  • A few popular keys with LRU: a 65% hit rate, only 7 database reads out of 20. Average latency falls from 50 ms to about 19 ms.
  • Scanning many keys once: 0% hits with every rule. A cache cannot help when keys do not repeat, and it just adds work.
  • Hot keys plus one-offs: LRU and LFU keep the hot keys A and B and reach 60%. FIFO throws out A and B because they arrived first, and drops to 40%.
  • Tick Flush the cache before request 11. This is a cache restart or crash. Hits drop and database reads rise from 7 to 9. In a real system with millions of requests, that surge can overload the database.

Caching strategies

These are the patterns for reading and writing through a cache.

StrategyHow it worksGood whenWeakness
Cache-aside (lazy loading)The app checks the cache. On a miss it reads the database and stores the result in the cacheRead-heavy data, the most common patternFirst read is slow, and the app must keep the cache correct
Read-throughThe cache itself loads from the database on a miss; the app only talks to the cacheYou want simpler app codeThe cache layer needs to know how to load; same cold start
Write-throughEvery write goes to the cache and the database together, before it is acknowledgedData read soon after it is written, and you want the cache always freshSlower writes; you cache items that may never be read
Write-behind (write-back)Write to the cache now, and save to the database later in the backgroundWrite-heavy workloads where fast writes matterData can be lost if the cache fails before it is saved
Write-aroundWrite to the database only; the cache fills on the next readData that is written once and rarely readThe first read after a write is a miss

Local cache vs shared cache

  • In-process (local) cache: lives inside each app server's memory. It is the fastest, with no network call, but each server holds its own copy, copies drift apart, and a restart empties it.
  • Shared (distributed) cache: a separate service such as Redis or Memcached that all servers use. One copy for everyone and it survives an app restart, but each lookup is a network call and it is one more thing to run.

Many systems use both: a small local cache for the hottest items, in front of a shared cache, in front of the database. A CDN (content delivery network) is the same idea at the edge: copies of pages and files cached in locations near users.

How cache eviction and invalidation work

A cache is small, so something has to go when it fills up. And a copy can become wrong when the source changes. Two separate rules handle those problems.

Eviction decides what to remove when the cache is full:

  • LRU (least recently used): remove the item not touched for the longest time. A good default.
  • LFU (least frequently used): remove the item used the fewest times. Good when some items are popular for a long time.
  • FIFO (first in, first out): remove the oldest item, whether or not it is still popular. Simple, but it can drop hot items.

Invalidation keeps copies from going stale:

  1. Expiry (TTL): every entry gets a time to live, for example five minutes. After that it is dropped, and the next read fetches fresh data. Simple, and it bounds how stale data can be.
  2. Delete on write: when the source changes, delete the cached copy (or update it). The next read reloads it.
  3. Versioned keys: put a version in the key, such as user:42:v7. A change creates a new key, and the old one expires.

Cache stampede happens when a popular entry expires and many requests miss at once, so they all hit the database together. Fixes: only let one request reload the entry while the others wait, add a little random jitter to TTLs so entries do not expire together, or refresh popular entries before they expire.

Caching vs the alternatives

ConcernCacheFaster query or indexRead replicasPrecomputed results
Lower read latencyYes, the biggest gainSomeSomeYes
Less load on the databaseYesSomeYesYes
Data always currentNo, it can be staleYesAlmost, replicas can lagNo, as fresh as the last run
Extra moving partYesNoYesYes (a job)
Needs app changesYesNoSomeSome
Best forRepeated reads of the same dataSlow queriesSpreading read trafficExpensive reports and feeds

A cache is the right first step for repeated reads. If one query is slow for everyone, fix the query. If you need current data on many reads, replicas are safer.

Avoiding a new single point of failure

A cache is meant to be optional, but systems can come to depend on it. If the cache empties, every request goes to the database at once, which can take it down. Protect against that:

  • Treat the cache as disposable. The database must be able to survive a cold cache, or you need a plan such as rate limiting or serving stale data.
  • Run the cache redundantly (replicas or a cluster) so one node failing does not empty everything.
  • Warm it up before sending full traffic after a restart.
  • Fail open: if the cache is down, read from the database, and protect the database with limits.

The trade-off: you gain speed and lower load, and accept stale data and one more system whose failure you must plan for.

Common mistakes

  • No expiry. Entries live forever and go stale. Always set a TTL, even a long one.
  • Caching data that does not repeat. It wastes memory and adds latency. Check the hit rate before you celebrate.
  • Forgetting to invalidate on write. Users see old data after an update. Delete or update the entry when the source changes.
  • Caching user-specific data under a shared key. One user can see another's data. Include the user in the key, or do not cache it.
  • Ignoring stampedes. A hot key expiring can flood the database. Use locking, jitter or early refresh.
  • Treating the cache as the source of truth. It can be emptied at any time. The database stays authoritative.

How to talk about caching in an interview

  • Say what you cache and why: "product pages are read much more than written, so cache them."
  • Pick a pattern: "cache-aside with a five-minute TTL, delete on write", and say what you give up.
  • Say where it sits: local, shared, CDN, or several layers.
  • Mention unprompted: eviction (LRU), stampede, what happens on a cold cache, and how you will measure the hit rate.
  • Name the trade-off: speed and lower load in return for staleness and complexity.

Where you meet caching in real systems

Almost every fast system has several layers of caches. Here are three drawn out, the first with configuration you can run, then more examples.

Example 1: A web app with a proxy cache and a Redis cache

A common setup has a reverse proxy such as NGINX that caches whole responses, and an in-memory store such as Redis that caches data the app reads often. Open the example below to see the picture and the configuration.

Config: cache pages in NGINX and data in RedisSetup 2 small config filesCode no code changes
Browserasks for a pageNGINX cachehit: doneApp + Redishit: doneDatabaselast resort
NGINX answers repeated page requests itself. Misses go to the app, which checks Redis first, and only a Redis miss reaches the database.
# NGINX as a caching reverse proxy in front of an app server (a cache in front of the origin).
events {}
http {
    # Where cached files live, how big the cache can grow, and how long unused items are kept.
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app_cache:10m max_size=1g inactive=60m;

    server {
        listen 80;

        location / {
            proxy_pass http://10.0.0.11:8080;            # placeholder origin server address

            proxy_cache app_cache;                        # use the cache defined above
            proxy_cache_valid 200 302 10m;                # keep good answers for 10 minutes
            proxy_cache_valid 404 1m;                     # keep "not found" for 1 minute
            proxy_cache_use_stale error timeout updating; # serve an old copy if the origin is down or slow
            proxy_cache_lock on;                          # on a miss, send only one request to the origin

            add_header X-Cache-Status $upstream_cache_status;  # shows HIT or MISS, handy when debugging
        }
    }
}

The NGINX file was checked with nginx -t. The Redis settings were loaded by a real redis-server, and the commands were run with redis-cli. Hosts, sizes and times are placeholders to adjust for your own system.

Example 2: A CDN caching static files

A content delivery network keeps copies of images, scripts and videos in many locations. A user is served from a nearby copy, and only the first request in a region goes back to the origin server. The origin controls how long a copy may be kept with headers such as Cache-Control.

Userasks for logo.pngCDN edgenearby copy?Originonly on a missCDN keeps copyfor the TTL
The first request misses the CDN and goes to the origin, which sends the file with a Cache-Control header. The CDN keeps a copy and serves later users from it.

Example 3: A cold cache and a database flood

If a cache is flushed or restarts empty, every request misses at once. The database, which was protected by the cache, suddenly gets the full traffic. This is why teams warm caches before sending traffic and protect the database with limits.

Cache restartsnow emptyEvery request misseshit rate falls to 0Database floodsslow or downWarm up + limitsthe fix
With an empty cache every request is a miss, so the database receives all the traffic it used to be shielded from. Warm the cache first, and limit the load on the database.

More real-world scenarios

4. Product pages and catalogues

A shop's product page is read many times for each time it changes. Caching the page, or the data behind it, means most visitors never reach the database. A short TTL keeps prices reasonably fresh.

5. Social feeds and timelines

A feed is expensive to assemble from many sources. Services often build it ahead of time and keep recent feeds in memory, so opening the app is a quick read rather than a heavy query.

6. User sessions

Instead of storing login sessions in one web server's memory, sessions can live in a shared in-memory store, so any server can handle any request. Entries expire on their own after a period.

7. Browser and DNS caches

Your browser keeps copies of images and scripts, and your device and network remember recent DNS answers. Both are caches with expiry times set by the source.

8. Database buffer caches

Databases keep recently used pages in memory, so repeated reads do not touch the disk. It is the same idea inside the database engine, and it is why adding memory can speed one up.

9. Leaderboards and counters

A game can keep the top scores or a running count in memory and update it as events arrive, instead of recalculating from millions of rows on every request.

10. Hardware caches

The processor has small, very fast caches (L1, L2, L3) in front of main memory, using eviction rules like the ones in this lesson. The idea scales from chips to the whole internet.

Tools and managed services

KindNameNotes
Open sourceRedisIn-memory data store with expiry and eviction policies
Open sourceMemcachedSimple, fast in-memory key-value cache
Open sourceNGINX, VarnishCaching reverse proxies for whole responses
AWSAmazon ElastiCacheManaged Redis and Memcached
AWSAmazon CloudFrontCDN
Google CloudMemorystoreManaged Redis and Memcached
Google CloudCloud CDNCDN
AzureAzure Cache for RedisManaged Redis
AzureAzure Front DoorCDN and global entry point

The takeaway

Whenever a design has data that is read far more than it changes, the first box you draw is a cache in front of the database, with an expiry and a plan for when it is empty.

Interview questions

Try answering out loud before you reveal hints. Interviewers care about how you reason, not just the final answer.

  1. 1.

    Explain cache-aside and write-through. When would you pick each?

    easy
  2. 2.

    A popular cache entry expires and the database is overwhelmed by the sudden misses. What is this, and how do you prevent it?

    medium
  3. 3.

    You have a small cache and must pick LRU, LFU or FIFO. How do you decide?

    medium
  4. 4.

    How do you keep a cache and a database consistent when data is updated?

    medium
  5. 5.

    Design the caching for a busy online shop's product pages.

    medium

↑ Back to top

Next lesson