Skip to content
SparkTime, home
System Design From Zero to Interview-Ready
Lessons
1 · System Design Foundations
2 · APIs and Communication
3 · Capacity Estimation
4 · Core Building Blocks
5 · Databases
6 · Distributed Systems
7 · Reliability and Operations
8 · Architecture and Security
9 · Interview Method and Foundation Case Studies
10 · Real-World Case Studies
11 · Large-Scale Case Studies
12 · Interview Readiness
Lesson 9 of 45

Lesson 9 · Core Building Blocks

Caching in System Design: Patterns, Invalidation, Eviction and Redis

Caching as a design decision: where caches live, hit ratios, cache-aside, read-through, write-through and write-back, TTLs and invalidation, LRU and other eviction policies, cache stampedes, hot keys, negative caching and distributed caches such as Redis, with the trade-off between faster reads, stale data and invalidation complexity.

On this page

Key takeaways

  • Cache when the same data is read repeatedly, is expensive to produce, and can tolerate some staleness; the trade-off is always faster reads against stale data and invalidation work.
  • Cache-aside is the common default: read the cache, on a miss read the database and fill the cache; on a write, update the database and then delete the cached entry.
  • Every cached entry needs an expiry or an invalidation rule; add random jitter to TTLs so many keys do not expire together.
  • Protect the database from stampedes and hot keys with request coalescing, stale-while-revalidate, local caches and key replication.
  • Plan for the cache failing: the database must survive a cold cache, or the system must degrade gracefully.

In article 07, Pixly's feed needed about 35,000 metadata lookups per second at peak. Most of those lookups ask for the same few hundred thousand recent photos, over and over. Sending each one to the database is like walking to the library every time you want to re-read a page of a book on your desk.

A cache is the desk: a small, fast store that keeps copies of data you are likely to need again. It is the single most effective performance tool in system design, and also one of the easiest to get subtly wrong, because the moment you keep a copy, the copy can disagree with the original. This article teaches caching as a design decision: when it is worth it, which pattern to use, and how it fails.

What you will learn

#
  • Why caches work, and the layers where caches live.
  • Hits, misses and hit ratios, and what to cache and what not to.
  • Cache-aside, read-through, write-through, write-back and write-around, compared.
  • TTLs, invalidation strategies, and a race condition that serves stale data.
  • Eviction policies such as LRU and LFU, with a runnable LRU example.
  • Cache stampedes, hot keys and negative caching.
  • Distributed caches, Redis and Memcached, and how caches fail.

Why caching works

#

Two facts make caches effective:

  1. Memory is much faster than disks and networks. From article 03: a memory read is about 100 nanoseconds; a database query over the network takes milliseconds. A cache in memory answers hundreds or thousands of times faster than recomputing the answer.
  2. Access is skewed. A small fraction of data receives most of the reads: today's photos, the trending product, the popular profile. Caching that fraction serves most traffic with little memory (the working set from article 07).

The cost is equally fundamental: a cache is a copy, and copies go stale. Every caching decision balances three things:

Code
   faster reads  <---->  stale data  <---->  invalidation complexity

Where caches live

#

Caches appear at every layer of a system. A request may hit several before reaching the database.

Code
Browser / app cache       HTTP caching headers; data the app keeps locally     |CDN edge cache            static files and cacheable responses near users (article 10)     |Reverse proxy cache       whole responses at your edge     |Application in-process    a small map in each server's memory (fastest, not shared)     |Distributed cache         Redis or Memcached, shared by all servers     |Database                  has its own buffer cache of hot pages in memory

Layer

Strength

Weakness

Client

No network at all

You cannot invalidate it directly; only expiry or versioned URLs

CDN

Close to users, offloads bandwidth

Best for public or semi-public content

In-process

Nanoseconds, no network

Each server has its own copy; copies disagree; limited by server memory

Distributed

Shared, large, consistent across servers

A network hop; another system to run

Database buffer cache

Automatic

Still pays query parsing, network and locking costs

Most backend designs use a distributed cache as the main tier, sometimes with a small in-process cache in front for the very hottest keys.

Hits, misses and the hit ratio

#
  • A hit: the data is in the cache. Fast.
  • A miss: it is not. You fetch from the source, and usually store it in the cache for next time.
  • The hit ratio = hits / (hits + misses).

As article 07 showed, the database sees total reads x (1 - hit ratio). Going from 90% to 99% hits cuts database reads by ten times. Hit ratio is the number to watch.

What to cache

#

Good candidates share three properties:

  • Read often relative to how often they change (high read:write ratio).
  • Expensive to produce: a multi-table join, a computed recommendation, a call to a slow service.
  • Tolerant of some staleness: a few seconds or minutes of old data harms nobody.

Product pages, user profiles, photo metadata, configuration, rendered fragments and results of expensive queries are classic examples.

What not to cache (or cache carefully)

#
  • Data that must always be exact, such as an account balance used to authorise a payment. Read it from the source of truth.
  • Data that changes on almost every read: the hit ratio will be low and the cache only adds work.
  • Sensitive data in shared caches. Microsoft's guidance on cache-aside warns that caching may be inappropriate for sensitive or security-related data, especially when several applications or users share the cache (Cache-Aside pattern). A key-naming mistake can show one user's data to another.

Cache keys

#

A key must include everything that changes the answer. If a product page differs by language and currency, the key is product:9:lang=hi:cur=INR, not product:9. Forgetting an input is how users end up seeing someone else's language, or someone else's data.

Prefixing keys with a version (v3:product:9) lets you invalidate everything of one kind at once when the format changes: deploy code that reads v4: keys, and the old ones simply expire.

Caching patterns

#

Cache-aside (lazy loading)

#

The application manages the cache itself. This is the most common pattern and a good default.

Read:

  1. Look in the cache.
  2. On a hit, return it.
  3. On a miss, read the database, put the result in the cache with an expiry, return it.

Write:

  1. Write the change to the database.
  2. Delete the cached entry (rather than updating it).

Microsoft's description of the pattern follows the same steps, and notes that the order matters: update the data store before removing the item from the cache, otherwise a reader can repopulate the cache with the old value in between (Cache-Aside pattern).

Why delete instead of update? Updating the cache on every write wastes work for data nobody reads, and two concurrent writers can leave the cache holding the older value if their cache updates arrive in the opposite order to their database writes. Deleting lets the next reader load the truth.

Pros: only data that is actually read gets cached; the cache can fail without breaking reads (they fall back to the database). Cons: the first read after a miss is slow; there is a window of staleness; the application code carries the logic.

Read-through

#

The application talks only to the cache; on a miss, the cache itself loads from the database (through a loader you configure). The logic is the same as cache-aside, moved into a library or the cache product. It keeps application code cleaner, at the price of less control.

Write-through

#

Every write goes to the cache and the database as one operation (the cache layer writes through to the database before acknowledging). Reads after a write see the new value immediately.

Pros: cache is always fresh for written keys; good for read-after-write. Cons: every write pays both latencies; data written but never read still fills the cache. Usually combined with read-through.

Write-back (write-behind)

#

Writes go to the cache only, and the cache flushes them to the database later, often in batches.

Pros: very fast writes; batching reduces database load (useful for counters such as views and likes). Cons: if the cache loses data before flushing, writes are lost; the database lags behind. Use only for data you can afford to lose a little of, or with a durable cache.

Write-around

#

Writes go to the database only; the cache is filled on later reads (this is what cache-aside does on writes). Good when written data is rarely read soon after.

Pattern

Read latency

Write latency

Freshness

Risk

Typical use

Cache-aside

Miss is slow, hits fast

Database only, plus a delete

Stale until expiry or delete

Stale-set race (below)

General default

Read-through

Same as cache-aside

Depends on write pattern

Same

Coupled to the cache library

Same, with cleaner code

Write-through

Fast for written keys

Slower (two writes)

Fresh after write

Cache full of unread data

Read-after-write critical data

Write-back

Fast

Very fast

Database lags

Data loss if cache fails

Counters, analytics, high-volume non-critical writes

Write-around

Miss after write

Database only

Fresh on next load

First read slow

Write-heavy data rarely re-read

TTL and invalidation

#

Every cached entry needs a way to die. There are two ways, and you usually use both.

Time to live (TTL)

#

Each entry expires after a set time. Choose the TTL from the requirements: how stale may this data be?

Data

Reasonable TTL

Why

Product description

Hours

Rarely changes; also invalidated on edit

Price or stock shown on a listing

Seconds to a minute

Changes often; final check happens at checkout

User profile

Minutes, plus delete on edit

Edits must show soon

Feature configuration

30 to 60 seconds

Fast enough rollout of changes

Exchange rates

Per the data source's update frequency

Do not cache longer than the source updates

Add jitter. If you warm 100,000 keys at the same moment with a TTL of exactly 1 hour, they all expire in the same second an hour later, and the database takes all the misses at once. Use a TTL of "1 hour plus or minus a random few minutes".

Explicit invalidation

#

When data changes, delete (or update) the affected keys. The hard part is knowing which keys: one product change may affect its page, its listing in three categories, a search result and a "related products" block. Options:

  • Delete the obvious keys in the write path, and let TTLs clean up the rest.
  • Event-driven invalidation: the write publishes a change event; a consumer deletes affected keys. A variant reads the database's change log (change data capture, article 21) so no write path can forget.
  • Versioned keys: store a version number per entity; cache keys include it; bumping the version makes old entries unreachable.

A race that serves stale data

#

Even with "update the database, then delete the key", cache-aside has a classic race:

Code
Time  Reader                               Writer 1    cache miss for photo:1 2    reads old caption from database 3                                         updates caption in database 4                                         deletes photo:1 from cache 5    writes OLD caption into cache      -> cache now holds stale data until the TTL expires

The reader was slow between steps 2 and 5, and its stale value landed after the delete. Mitigations:

  • A TTL bounds how long the stale value lives. This is why cache-aside should always have one.
  • Delete again after a short delay (a "double delete").
  • Conditional set: only write to the cache if the key was not invalidated since the read began. Facebook's paper on scaling memcache describes "leases" for this purpose: the cache hands out a token on a miss, and a set is accepted only if the token is still valid, which also limits thundering herds (Scaling Memcache at Facebook).
  • Version checks: store the row's version with the cached value and refuse to overwrite a newer version.

In interviews, mentioning this race and one mitigation is a clear sign of depth.

Eviction: when the cache is full

#

Memory is finite. When the cache is full, it must evict something to make room. Common policies:

Policy

Evicts

Good for

LRU (least recently used)

The item unused for the longest time

Most workloads with recency skew; a sensible default

LFU (least frequently used)

The item used least often

Stable popularity; resists one-off scans pushing out popular items

FIFO

The oldest inserted

Simple; rarely best

Random

A random item

When access is roughly uniform; cheap

TTL-based

Items closest to expiry

When TTLs encode importance

Redis, for example, offers policies including noeviction, allkeys-lru, allkeys-lfu, allkeys-random, volatile-lru and volatile-ttl. Its documentation suggests allkeys-lru as a good default when you have no reason to prefer another, and explains that Redis approximates LRU by sampling keys rather than tracking exact order, because exact LRU costs more memory (Key eviction).

LRU and cache-aside in code

#

This Java program builds a tiny LRU cache (Java's LinkedHashMap can keep entries in access order and evict the eldest) and uses it with the cache-aside read and write rules. The "database" is a map, so the run is deterministic. As always, the language is incidental.

Java
// File: CacheAsideDemo.javaimport java.util.LinkedHashMap;import java.util.Map;public class CacheAsideDemo {    // LinkedHashMap in access order: the eldest entry is the least recently used.    static class LruCache<K, V> extends LinkedHashMap<K, V> {        private final int capacity;        LruCache(int capacity) {            super(16, 0.75f, true);            this.capacity = capacity;        }        @Override        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {            return size() > capacity;        }    }    static final Map<String, String> database = new LinkedHashMap<>();    static final LruCache<String, String> cache = new LruCache<>(3);    static int hits = 0;    static int misses = 0;    static String read(String key) {        String value = cache.get(key);        if (value != null) {            hits++;            return value;        }        misses++;        value = database.get(key);          // the slow path        if (value != null) {            cache.put(key, value);          // fill the cache for next time        }        return value;    }    static void update(String key, String value) {        database.put(key, value);           // 1. write the source of truth        cache.remove(key);                  // 2. then invalidate the copy    }    public static void main(String[] args) {        for (int i = 1; i <= 5; i++) {            database.put("photo:" + i, "caption " + i);        }        String[] requests = {"photo:1", "photo:2", "photo:1", "photo:3", "photo:1", "photo:4", "photo:2", "photo:1"};        for (String key : requests) {            read(key);            System.out.println("read " + key + " -> cache " + cache.keySet());        }        update("photo:1", "new caption");        System.out.println("after update: " + read("photo:1"));        System.out.println("hits=" + hits + " misses=" + misses);    }}
Code
read photo:1 -> cache [photo:1]read photo:2 -> cache [photo:1, photo:2]read photo:1 -> cache [photo:2, photo:1]read photo:3 -> cache [photo:2, photo:1, photo:3]read photo:1 -> cache [photo:2, photo:3, photo:1]read photo:4 -> cache [photo:3, photo:1, photo:4]read photo:2 -> cache [photo:1, photo:4, photo:2]read photo:1 -> cache [photo:4, photo:2, photo:1]after update: new captionhits=3 misses=6

Follow the list: each read moves the key to the end (most recent). When photo:4 arrives and the cache is full, the eldest (photo:2) is evicted. photo:1, read often, survives. After the update deletes photo:1, the next read misses and loads the new caption, which is exactly the cache-aside write rule at work.

When caches hurt: stampedes, hot keys and empty results

#

Cache stampede (thundering herd)

#

A very popular key expires. In the next instant, thousands of requests miss at the same time, and all of them query the database for the same row. Suppose a key receives 50,000 requests per second and rebuilding it takes 200 ms: without protection, up to 50,000 x 0.2 = 10,000 identical database queries arrive before the first one refills the cache. The database slows down, rebuilding takes longer, more requests pile in, and the cache never recovers.

Mitigations:

  • Request coalescing (single flight): on a miss, only one request rebuilds the value; others wait for it or are served the previous value. Within one server this is a lock per key; across servers, a short-lived lock in the cache itself.
  • Serve stale while revalidating: keep the expired value briefly and return it while one background refresh fetches the new one.
  • Early refresh: refresh popular keys a little before they expire, for example with a probability that rises as expiry approaches.
  • TTL jitter, so keys do not expire together.
  • Pre-warming caches before known events and after deployments.

The Amazon Builders' Library article on caching discusses these risks, including how a cache can hide how much load a service would face without it (Caching challenges and strategies).

Hot keys

#

Sometimes one key is so popular that the single cache node holding it is saturated: a celebrity's profile, a flash-sale product, the score of a final. Adding more cache nodes does not help, because the key lives on one of them.

Mitigations:

  • A small in-process cache in each application server for the hottest keys, with a very short TTL (even one second removes most of the traffic).
  • Key replication: store copies as product:9#1 to product:9#8 on different nodes and read a random copy.
  • Read replicas of the cache node.

Negative caching

#

If many requests ask for something that does not exist (a deleted photo, a mistyped URL, or an attacker probing random ids), every one misses the cache and hits the database. Cache the "not found" result too, with a short TTL. Combine it with input validation and rate limiting so attackers cannot fill your cache with junk keys.

Distributed caches

#

A distributed cache runs on its own servers and is shared by every application server. That is what stateless application tiers need: any server sees the same cached data.

When one node is not enough, the cache is partitioned (sharded) across nodes, each holding part of the keys, and replicated so that a node failure does not lose its share. Redis Cluster, for example, divides the key space into 16,384 hash slots spread across primary nodes, each with replicas (Redis cluster specification). Article 32 designs a distributed cache from scratch, including consistent hashing.

Redis and Memcached

#

Redis (and compatible forks such as Valkey)

Memcached

Data model

Strings plus data structures: lists, hashes, sets, sorted sets, streams, geospatial indexes

Simple key-value strings

Extra features

Atomic operations, Lua scripting, pub/sub, optional persistence, replication, clustering

Focused on caching only

Typical use

Caching plus counters, rate limiting, leaderboards, sessions, queues

Pure, simple caching at scale

Redis's data structures make it useful well beyond caching (articles 31 and 35 use sorted sets and atomic counters). That flexibility is also a trap: if you use Redis as the only copy of important data, it is a database and must be treated like one, with persistence, replication and backups.

Managed versions exist on every major cloud (for example Amazon ElastiCache, Google Memorystore, Azure Managed Redis), but the concepts are the same whether you run it yourself or not.

A Redis cache-aside fragment

#

In a Java service, cache-aside with Redis often looks like this. It is an illustration of the pattern; client libraries differ.

Java
public Photo getPhoto(long photoId) {    String key = "v1:photo:" + photoId;    String cached = redis.get(key);    if (cached != null) {        return Photo.fromJson(cached);                 // hit    }    Photo photo = photoRepository.findById(photoId);    // miss: go to the database    if (photo != null) {        int ttlSeconds = 3600 + random.nextInt(300);    // one hour plus jitter        redis.setex(key, ttlSeconds, photo.toJson());    } else {        redis.setex(key, 60, "null");                    // short negative cache    }    return photo;}public void updateCaption(long photoId, String caption) {    photoRepository.updateCaption(photoId, caption);     // source of truth first    redis.del("v1:photo:" + photoId);                    // then invalidate}

Consistency and caching

#

A cache adds a second copy, so readers can see older data than the database holds. Decide per feature:

  • Eventual consistency is fine for most cached data (profiles, product descriptions, counts).
  • Read-your-own-writes for the person who made a change: after a user edits their caption, read their own data from the database (or write through) for a short time, so they never see the old caption.
  • Strong consistency is required for decisions with consequences (stock at checkout, balance before payment). Use the cache to display, but decide using the source of truth.

When the cache fails

#

Ask "what happens if the cache disappears?" for every design.

  • Cache cluster down. Every read goes to the database. If the database was sized assuming a 95% hit ratio, it now sees twenty times its normal read load. Either size the database to survive this, or degrade gracefully (serve fewer features, rate-limit, show a simplified page) and protect it with circuit breakers (article 23).
  • Cold cache after a restart or failover. The same stampede problem, briefly. Warm critical keys before taking traffic, or ramp traffic gradually.
  • Cache node loss in a cluster. A share of keys misses until replicas take over or the keys are reloaded.
  • Stale or wrong data. Bugs in invalidation or keys show old or wrong data. Keep TTLs on everything so mistakes heal themselves.

A cache can also improve availability: if the database is briefly down, serving slightly stale cached data may be better than serving errors, for content where that is acceptable.

Security considerations

#
  • Keys must include the user or tenant for personal data, so one user's data is never served to another.
  • Do not cache authorisation decisions longer than you can tolerate a revoked permission still working.
  • Keep the cache on a private network with authentication; an exposed cache can leak data or be filled with poisoned entries.
  • Be careful caching responses that vary by cookies or headers at a shared layer (article 10 covers CDN cache keys).

Worked example: caching for Pixly

#

Data

Pattern

TTL and invalidation

Reason

Photo metadata

Cache-aside

1 hour with jitter; delete on edit

Read about 35,000 times per second at peak; changes rarely

User profile summary

Cache-aside

10 minutes; delete on edit; uploader reads own profile from the database for a minute after editing

Read on every feed item

Like counts

Write-back style: increment in Redis, flush to the database every few seconds

Counts may lag

1,700 like writes per second at peak; exactness not required

Sessions

Stored in Redis (it is the session store, not a cache)

Session expiry

Lets API servers stay stateless (article 08)

Precomputed feeds

Stored in Redis as lists of photo ids

Rebuilt from the database if missing

Article 35 discusses this choice

Celebrity profiles

Local in-process cache in front of Redis

2 seconds

Hot keys

Sizing from article 07: about 14 GB for 7 days of metadata, more if feeds are precomputed, on a small replicated Redis cluster. If Redis fails, the database must handle about 35,000 lookups per second at peak; it cannot, so Pixly protects it with request coalescing and a degraded feed (recent photos from fewer followed users) until the cache is warm again.

Trade-offs

#

Decision

You gain

You pay

Adding a cache

Lower latency, much lower database load

Staleness, invalidation logic, another system to run, a cold-cache risk

Longer TTL

Higher hit ratio

Staler data

Write-back

Fast writes, batching

Possible data loss

Write-through

Fresh reads after writes

Slower writes, cache full of unread data

In-process cache

Fastest reads

Inconsistent copies across servers

Bigger cache

Higher hit ratio

Memory cost, with diminishing returns once the hot set fits

Common mistakes

#
  • Caching before measuring whether reads are actually the bottleneck. Sometimes an index (article 13) is the right fix.
  • No TTL "because we invalidate on write". One missed invalidation and the data is stale forever.
  • Updating instead of deleting on writes, and racing writers.
  • Keys that omit inputs (language, user, version).
  • Treating the cache as the source of truth for data you cannot lose.
  • Sizing the database as if the cache can never fail.
  • All keys expiring at the same moment.

In an interview

#
  • Introduce the cache with a reason: "reads are 3,500 per second at peak and mostly repeat; a cache with a 95% hit ratio drops database reads to under 200 per second".
  • Name the pattern (usually cache-aside), the TTL, and how writes invalidate.
  • Say what may be stale and why that is acceptable.
  • Mention one failure: stampede on a hot key, or the database's survival when the cache is down.

Exercises

#

Exercise 1

Choose the pattern

#

Choose a caching pattern (or no cache) and justify it:

  1. A product catalogue read 50,000 times per second, edited a few hundred times a day.
  2. A "views" counter on articles, incremented on every page view.
  3. A user's bank balance shown before confirming a transfer.
  4. A user's own settings page, where they expect changes to appear immediately after saving.

Exercise 2: The stale caption

#

Users report that after editing a caption, the old caption sometimes reappears for exactly one hour. The code updates the database and then deletes the cache key. Explain how this happens and propose two fixes.

Exercise 3: Stampede arithmetic

#

A home-page key receives 20,000 requests per second. Rebuilding it takes 300 ms and costs one database query. The key has a TTL of 10 minutes with no protection.

  1. How many database queries can one expiry cause?
  2. How many expiries per day, and how many extra queries does that add per day?
  3. Describe a design that reduces each expiry to a single query.

Exercise 4: Hot key

#

During a flash sale, one product's cache key receives 300,000 reads per second. Your cache nodes each handle about 100,000 operations per second. What happens, and give two fixes.

Show solutionHide solution
  1. Cache-aside (or read-through) with a long TTL (hours, with jitter) and deletion on edit. The read:write ratio is enormous, and slightly stale descriptions harm nobody. Consider a CDN for the rendered pages too.
  2. Write-back counter. Increment in Redis (atomic INCR) and flush totals to the database every few seconds or minutes. Losing a few seconds of views in a cache failure is acceptable; writing to the database on every view is wasteful.
  3. No cache for the decision. Display can use a short-lived cached value, but the transfer must check the balance in the source of truth, inside a transaction (article 14). A stale balance could authorise a transfer the account cannot cover.
  4. Cache-aside with read-your-own-writes. After saving, read this user's settings from the database (or write through to the cache) so they see their change immediately. Other readers, if any, can tolerate the normal TTL.

Exercise 2

Show solutionHide solution

This is the stale-set race. A reader missed the cache and read the old caption from the database. Before it wrote that value into the cache, the editor's request updated the database and deleted the key. Then the slow reader wrote the old caption into the cache, where it lived for the full one-hour TTL.

Fixes (any two):

  • Shorter TTL for this data, which bounds the damage.
  • Delayed second delete: delete the key again a second or two after the update.
  • Versioned writes: store the row's version with the cached value; a set carrying an older version than the one recorded is rejected.
  • Lease or token on miss: only accept the cache write if no invalidation happened since the miss.
  • Read-your-own-writes for the editor: at least the person who edited never sees the stale value, even if others briefly do.

Exercise 3

Show solutionHide solution
  1. During the 300 ms rebuild, requests keep missing: 20,000 x 0.3 = up to 6,000 database queries for the same data.
  2. A 10-minute TTL means 144 expiries per day. 144 x 6,000 = about 864,000 extra queries per day, all concentrated in sharp bursts that can slow the database for everyone.
  3. Use request coalescing plus stale-while-revalidate: keep serving the previous value after it expires, and let exactly one request (protected by a short lock, for example a Redis SET key NX with a timeout) rebuild it. Better still, refresh proactively: a background job rebuilds the home page every few minutes, so user requests never trigger a rebuild. Add TTL jitter for other keys.

Exercise 4

Show solutionHide solution

All reads for one key go to the one node (and its replica, if replicas serve reads) that holds it. 300,000 operations per second is three times that node's capacity, so it saturates: latency rises, requests time out, and every other key on the same node suffers too. Adding nodes does nothing, because the key does not move.

Fixes:

  • Local in-process cache on every application server with a TTL of 1 to 5 seconds. With 100 application servers, each fetches the key from Redis at most once per second or so; Redis sees about 100 reads per second instead of 300,000.
  • Key replication: write the product to product:9#1 to product:9#10 on different nodes, and read a random suffix, spreading the load across ten nodes.
  • Also: serve the product page itself from a CDN with a short TTL (article 10), so most reads never reach your servers at all.

Where to go next

#

Sources & further reading

  1. learn.microsoft.com
  2. redis.io
  3. redis.io
  4. aws.amazon.com
  5. usenix.org
  • Guide · Technology

    Start Here: Your Roadmap from Zero to Job-Ready DSA

  • Guide · Technology

    Heaps and Priority Queues

  • Guide · Career

    A LinkedIn Profile That Gets Recruiter Messages