Caching & CDN for System Design

Author Photo
Caching and CDN for System Design

The fastest work a system can do is the work it never has to repeat. Every topic in this post is built on that single idea: keep a copy of a result close to where it's needed, so you don't recompute or refetch it every time. You will start with Caching โ€” storing frequently-used data in fast memory so repeat requests skip the slow database entirely, including how cache hits and misses work, how TTL and eviction keep a cache fresh and bounded, and the write-through and cache-aside strategies. Then you will learn about the CDN โ€” a network of edge servers spread around the world that deliver static content from a location physically close to each user, so a shopper in Tokyo isn't waiting on a server in Virginia. Finally you will take a deep dive into Redis โ€” the in-memory data store behind most caches โ€” covering what it is, how it works, why it's astonishingly fast, and when to reach for it. Each topic is explained with real-world analogies, step-by-step examples, and clear diagrams โ€” starting from zero.

โšก 1. Caching

A cache is a small, fast store that holds copies of data you've fetched or computed before, so the next request for the same data is answered instantly instead of hitting a slow database or recomputing a result. In this section you will learn what a cache is and where it lives, how a cache hit and cache miss play out, how TTL and eviction policies keep it fresh and within memory limits, the main read and write strategies (cache-aside, write-through, write-back), and the hardest problem in caching โ€” invalidation, keeping cached copies from going stale.

Caching โ€” storing frequently used data in fast memory for instant repeat access

1.1 ๐ŸŽฏ Introduction

Every time a shopper opens a product page on our online store ShopEasy, the app fetches the same details โ€” name, price, description, rating. For a popular laptop viewed 100,000 times a day, that's 100,000 identical database queries returning the exact same answer. The database does a lot of work to tell everyone the same thing. A cache fixes this: fetch the product once, keep a copy in fast memory, and serve the next 99,999 requests from that copy in a fraction of a millisecond โ€” never touching the database.

A cache is a small, fast storage layer that sits in front of a slower source of truth (a database, an API, a computation) and holds copies of recently or frequently used data. When the data you want is already in the cache, that's a cache hit and you get it instantly. When it isn't, that's a cache miss, and you fall back to the slow source โ€” then usually store the result in the cache so next time is a hit.

Caches are fast for two reasons: they live in memory (RAM) instead of on disk, and they store data by a simple key so lookups are direct โ€” no scanning, no joins. The trade is that memory is small and expensive, so a cache holds only a hot subset of the data, and that copy can drift out of date โ€” which is what makes caching both powerful and tricky.

๐Ÿ’ก Cache = a fast copy, not the source of truth. The database still owns the real data. A cache just keeps a nearby copy of the parts you use most, so you avoid asking the slow source over and over. Everything about caching flows from that one idea.

1.2 ๐Ÿ’ก Why It Matters

Caching is one of the highest-leverage tricks in system design โ€” a small cache can absorb the vast majority of traffic and make a system both faster and cheaper at once. The speed gap is enormous: reading from RAM takes around 100 nanoseconds, while reading from a database over the network can take 1โ€“10 milliseconds โ€” roughly 10,000ร— to 100,000ร— slower. Real systems lean on this hard: Facebook runs one of the world's largest memcached deployments to serve billions of requests, and a well-tuned cache commonly serves 80โ€“95% of reads without ever touching the database.

  • Speed โ€” cache hits return in microseconds, turning a sluggish page into an instant one.
  • Reduced load โ€” every hit is a query the database never sees, which is often the difference between a database that copes and one that falls over (recall the database bottleneck from the scalability post).
  • Lower cost โ€” serving from a cheap cache node is far less expensive than scaling the database to handle every read.
  • Better experience โ€” faster responses directly improve engagement and conversions; slow pages lose users.
Why system design cares: "add a cache" is one of the first moves in almost every design, because it attacks the most common bottleneck (the database) cheaply. But it comes with a famous catch โ€” keeping cached data from going stale โ€” which is why interviewers and engineers care as much about invalidation as about the speed win.

1.3 ๐Ÿ  Real-world Analogy

Think of a chef working a busy dinner service. The pantry and the walk-in freezer are the database โ€” everything is there, but fetching an ingredient means a trip across the kitchen. So the chef keeps a small prep station right at the counter, stocked with the ingredients used most that night: chopped onions, grated cheese, the sauce of the day. That prep station is the cache.

When an order needs cheese and it's already on the station, that's a cache hit โ€” grabbed in a second. When it needs a rare spice that isn't there, that's a cache miss โ€” a trip to the pantry, and the chef brings back a little extra to keep on the station for next time. The station only has so much room (memory limit), so items that haven't been touched in a while get cleared to make space (eviction), and anything perishable is tossed after a set time (TTL).

Kitchen WorldCaching WorldMeaning
๐Ÿฅซ The pantry / walk-in freezer๐Ÿ—„๏ธ Database (source of truth)Everything, but slower to reach
๐Ÿ”ช The prep station at the counterโšก Cache (fast, in memory)A small hot subset, instant to grab
๐Ÿง€ Needed item already on the stationโœ… Cache hitServed instantly, no pantry trip
๐ŸŒถ๏ธ Rare item not on the stationโŒ Cache missFetch from the source, then stock it
๐Ÿงน Clearing unused items for space๐Ÿ—‘๏ธ EvictionDrop cold data when memory is full
โฒ๏ธ Tossing perishables after a whileโณ TTL (time to live)Expire entries so they don't go stale

The chef's whole trick is prediction: keep the things you'll reach for again within arm's reach, and don't waste counter space on what you won't. A cache does exactly this for data โ€” and just like a station holding yesterday's sauce, its danger is serving something that's no longer fresh.

1.4 ๐Ÿ“– Key Terms

TermSimple DefinitionQuick Example
CacheA fast, in-memory store of copied dataRedis holding product details
Cache HitRequested data is found in the cacheProduct served from Redis
Cache MissData isn't in the cache; fall back to the sourceFirst view fetches from the DB
Hit RatioShare of requests served from cache92% hits, 8% misses
TTL (Time To Live)How long an entry stays before expiringCache a price for 60 seconds
EvictionRemoving entries when memory is fullDrop the least-recently-used key
LRUEvict the Least Recently Used entryCommon default eviction policy
InvalidationRemoving/updating a cached copy that changedDelete the key when price updates
Cache-AsideApp checks cache, loads from DB on a missThe most common read pattern
Write-ThroughWrite to cache and DB togetherCache always matches the DB
Stale DataA cached copy that no longer matches the sourceOld price shown after a change

1.5 ๐Ÿ”ข How It Works

Let us break caching into five pieces: the hit/miss read path, how TTL and eviction keep the cache bounded and fresh, how reads are wired up (cache-aside), how writes are handled (write-through and write-back), and the hardest part โ€” invalidation.

A. Cache Hit & Cache Miss

Every cached read follows the same branch. The app asks the cache for a key. On a hit, the value comes straight back. On a miss, the app fetches from the database, returns it to the user, and populates the cache so the next request is a hit.

# Read path for product 42 value = cache.get("product:42") if value is not None: # โœ… HIT โ†’ return instantly (microseconds) return value else: # โŒ MISS โ†’ fetch from DB, then cache it value = db.query("SELECT * FROM products WHERE id = 42") cache.set("product:42", value, ttl=60) return value

The hit ratio โ€” the share of requests served from cache โ€” is the number that matters. A 95% hit ratio means the database sees only 1 in 20 reads. Cold caches start at 0% hits and "warm up" as popular data gets loaded; the goal is to keep the hot working set resident.

B. TTL & Eviction

Memory is limited, so a cache can't keep everything forever. Two mechanisms keep it bounded and reasonably fresh. TTL (time to live) stamps each entry with an expiry โ€” after, say, 60 seconds the entry is discarded and the next read is a miss that reloads fresh data. TTL is the simplest defence against staleness: even if you forget to invalidate, data self-corrects within the TTL window.

Eviction handles the "cache is full" case: when there's no room for a new entry, the cache drops an existing one according to a policy:

PolicyEvictsโ€ฆBest for
LRU (Least Recently Used)The entry unused for the longest timeGeneral purpose โ€” the common default
LFU (Least Frequently Used)The entry accessed the fewest timesStable, skewed popularity
FIFO (First In First Out)The oldest entry, regardless of useSimple, order-based needs
TTL-onlyNothing early โ€” entries just expireSmall caches that fit in memory

C. Read Strategy โ€” Cache-Aside

The most common pattern is cache-aside (also called lazy loading), which is exactly the read path above: the application manages the cache. It looks in the cache first, and only on a miss does it load from the database and backfill the cache. The cache stays "on the side" โ€” it never talks to the database itself.

The upside is that only requested data is ever cached (no wasted memory) and a cache failure isn't fatal โ€” the app just reads from the database. The downsides: the first request for any key is always a miss (a slower cold read), and it's the app's job to invalidate entries when data changes.

D. Write Strategies

Reads are only half the story โ€” when data changes, the cache and database can disagree. Three strategies handle writes differently:

  • Write-through โ€” write to the cache and the database at the same time, synchronously. The cache is always in sync, so reads are never stale, but every write pays the cost of updating both.
  • Write-back (write-behind) โ€” write to the cache immediately and flush to the database later, in the background. Very fast writes, but if the cache dies before flushing, you lose data โ€” risky for anything that matters.
  • Write-around โ€” write straight to the database and skip the cache; the data only enters the cache later, on a read miss. Good when freshly-written data isn't read right away.
# Write-through: update both, cache stays consistent cache.set("product:42", new_value) db.update("products", 42, new_value) # both written together

In practice, cache-aside reads + write-through (or explicit invalidation) on writes is the workhorse combination for most web systems.

E. Invalidation โ€” The Hard Part

There's a famous quip: "there are only two hard things in computer science โ€” cache invalidation and naming things." Invalidation is making sure a cached copy doesn't outlive the truth. When a product's price changes in the database, any cached copy is now stale and must be updated or deleted, or customers see the old price.

There are several invalidation strategies, and real systems usually combine two or three. They trade off freshness, write cost, and implementation effort differently:

StrategyHow it keeps the cache correctTrade-off
TTL expirationEach entry auto-expires after a set time; the next read reloads fresh dataSimplest; data can be stale for up to the TTL window
Write-throughEvery write updates cache and DB together, so the cache is never behindAlways fresh; every write pays to update both
Write-around + invalidateOn a write, delete (or update) the affected key so the next read reloads itPrecise and cheap; you must catch every write path โ€” easy to miss one
Event-basedA data change publishes an event; subscribers evict the affected keys everywhereScales to many caches; adds a messaging system to build and run
Versioned keysChange the key when data changes (product:42:v3) so old copies are simply never readNo purge needed; requires a version bump on every change

The most common production recipe is TTL + explicit invalidation: delete the key the instant data changes (precise and immediate), and lean on a bounded TTL as a safety net in case an invalidation is ever missed. Beyond staleness, two failure modes bite at scale:

  • Cache stampede โ€” a hot key expires and thousands of simultaneous misses all hammer the database at once. Mitigated by staggered TTLs or letting only one request rebuild the entry.
  • Thundering herd on a cold cache โ€” after a cache restart, everything is a miss and the database is briefly overwhelmed. Mitigated by warming the cache with popular keys ahead of time.
๐Ÿ“Œ The freshness dial: a longer TTL means more hits but staler data; a shorter TTL means fresher data but more misses. Choosing the TTL is really choosing how much staleness your feature can tolerate โ€” seconds for a price, minutes for a product description, hours for a rarely-changing config.

1.6 ๐Ÿ”€ Types & Variations

Caching isn't one thing in one place โ€” data can be cached at every layer between the user and the database. A request may be served by a cache long before it ever reaches your servers.

๐ŸŒ

Browser Cache

The user's own device stores static assets (images, CSS, JS) so repeat visits load instantly, with zero network request.

๐ŸŒ

CDN Cache

Edge servers around the world cache static content close to users. The subject of the CDN sub-topic later in this post.

โšก

Application Cache

A shared in-memory store (Redis, Memcached) holding query results and objects. The classic "the cache" in system design.

๐Ÿ—„๏ธ

Database Cache

The database's own buffer pool keeps hot rows and query plans in memory, so common queries avoid disk reads.

They stack. A single page load might hit the browser cache for the logo, a CDN for product images, an application cache (Redis) for the price, and the database's own buffer for anything left. Each layer catches what the layer above missed โ€” and the closer to the user a cache sits, the bigger the win.

1.7 ๐ŸŽจ Illustrated Diagram

The diagram shows the cache-aside read path. The app always asks the cache first. On a hit, it returns immediately. On a miss, it falls through to the database, then writes the result back into the cache so the next request is a hit.

%%{init: {"theme": "base", "themeVariables": {"lineColor": "#64748b", "edgeLabelBackground": "#fff"}}}%% flowchart TD A["โš™๏ธ App Server"] C["โšก Cache (Redis)"] DB["๐Ÿ—„๏ธ Database"] A -->|"1. get product:42"| C C -->|"2a. HIT โ†’ return value"| A C -->|"2b. MISS"| A A -->|"3. query on miss"| DB DB -->|"4. row"| A A -->|"5. set product:42 (ttl)"| C style A fill:#dbeafe,stroke:#2563eb,color:#1e3a8a style C fill:#d1fae5,stroke:#059669,color:#064e3b style DB fill:#fff3e0,stroke:#d97706,color:#92400e

Reading the diagram: the happy path is steps 1โ€“2a โ€” a hit that never touches the database. Steps 2bโ€“5 are the miss path, paid only once per key per TTL window; every subsequent read for that key is a fast hit until it expires or is evicted.

1.8 โœ… When to Use

Caching shines for data that's read far more than it's written and can tolerate being a little out of date. It's a poor fit for data that must always be exact or changes constantly.

Cache it whenโ€ฆDon't cache whenโ€ฆ
Data is read often and changes rarely (product info, config)Data must always be perfectly current (account balance)
The same result is requested repeatedlyEvery request is unique (no repeat reads to save)
The source is slow or expensive (heavy query, external API)The source is already trivially fast
Brief staleness is acceptable (seconds to minutes)Even momentary staleness is dangerous or illegal
Read load is overwhelming the databaseWrites dominate and reads are rare

A quick guide to picking a write strategy once you've decided to cache:

StrategyChoose it whenโ€ฆ
Cache-aside + TTLThe default โ€” read-heavy data where bounded staleness is fine
Write-throughReads must never be stale and you'll accept slightly slower writes
Write-backWrite speed is critical and rare data loss is tolerable (metrics, counters)
Explicit invalidationData changes at known moments you can hook into (price update โ†’ delete key)
Rule of thumb: start with cache-aside and a TTL sized to how much staleness the feature can bear. Add explicit invalidation for the data whose changes users notice immediately. Don't cache highly dynamic or must-be-exact data โ€” a wrong cached value is worse than a slightly slower correct one.

1.9 ๐Ÿ—๏ธ Real-world Example โ€” Caching a ShopEasy Product Page

A viral laptop deal sends 100,000 shoppers to the same product page in an hour. Follow how a Redis cache turns that flood into almost no database load:

StepActorWhat Happens
โ‘ ๐Ÿ‘ค First shopperRequests product:42; cache is empty โ†’ a miss
โ‘ก๐Ÿ—„๏ธ DatabaseServes the product row once; the app stores it in Redis with a 60s TTL
โ‘ข๐Ÿ‘ฅ Next 99,999 shoppersAll get a hit from Redis in microseconds โ€” the database sees none of them
โ‘ฃ๐Ÿ’ฐ MerchantChanges the price; the app invalidates the key (deletes product:42)
โ‘ค๐Ÿ‘ค Next shopperMiss again โ†’ reloads the new price from the DB and re-caches it
โ‘ฅโณ Cache (TTL)Even without step โ‘ฃ, the entry would expire after 60s and refresh on its own
The payoff: of 100,000 requests, the database handled roughly one. That's a 99.999% hit ratio for this key โ€” the database is free to do real work while the cache absorbs the storm. Invalidation on price change (step โ‘ฃ) plus the TTL safety net (step โ‘ฅ) keep shoppers from ever seeing a stale price for long.

1.10 โš–๏ธ Trade-offs

A cache is one of the best speed-for-effort deals in system design, but it adds a second copy of your data โ€” and two copies can disagree. The trade-offs:

โœ… AdvantagesโŒ Disadvantages
Huge speed gain โ€” microsecond reads instead of millisecondsStale data risk โ€” the copy can drift from the source
Offloads the database โ€” most reads never reach itInvalidation complexity โ€” keeping the cache correct is genuinely hard
Lower cost โ€” a cache node is cheaper than scaling the DBExtra moving part โ€” one more system to run and monitor
Absorbs spikes โ€” hot data is served from memory under loadCold-start & stampede โ€” mass misses can overwhelm the source
Improves user experience โ€” faster pages, higher engagementMore memory โ€” caches consume RAM, a limited resource
๐Ÿ“Œ The core tension: caching trades correctness guarantees for speed. The cached value is only probably current. Your whole caching design is really an answer to one question: how much staleness can this data tolerate, and how do I bound it?

1.11 ๐Ÿšซ Common Mistakes

#โŒ Common Mistakeโœ… The Reality
1 Forgetting to invalidate Caching a value with no TTL and no invalidation serves stale data forever. Always set a TTL and invalidate on change.
2 Caching data that must be exact Account balances or stock counts shown from a stale cache cause real errors. Read must-be-exact data from the source.
3 No expiry at all Entries that never expire fill memory and drift stale. A TTL is a safety net even when you also invalidate explicitly.
4 Ignoring cache stampede When a hot key expires, thousands of misses hit the DB at once. Stagger TTLs or let one request rebuild the entry.
5 Treating the cache as durable storage A cache can evict or lose data anytime. It's a fast copy, never the source of truth โ€” the DB must still hold the real data.
6 Caching everything Caching rarely-read or unique data wastes memory and lowers the hit ratio. Cache the hot, repeated, read-heavy data only.

1.12 ๐Ÿ“ Summary

  • A cache is a fast copy โ€” an in-memory store in front of a slower source; a hit is instant, a miss falls back to the source and backfills.
  • Hit ratio is the metric โ€” a high hit ratio means the database barely sees your reads.
  • TTL and eviction keep it bounded โ€” entries expire on a timer and cold data is dropped (LRU) when memory fills.
  • Cache-aside + write-through/invalidation โ€” the workhorse patterns for reading and keeping the cache correct.
  • Invalidation is the hard part โ€” the whole design is a choice of how much staleness the data can tolerate, bounded by TTL.

1.13 ๐Ÿ‹๏ธ Design Challenge

๐Ÿ“ฐ Challenge: Add caching to a news website

A news site shows article pages, a homepage list of headlines, and a per-user "recommended for you" feed. Traffic is huge during breaking news. Think through the following:
  • Which of the three โ€” article, headline list, recommendations โ€” should you cache, and which is riskiest to cache?
  • A breaking story is being edited every few minutes. What TTL or invalidation approach keeps it reasonably fresh?
  • When a huge story breaks, its cache entry expires and a million readers hit it at once. What problem is this, and how do you prevent it?
  • Would you cache the personalised feed the same way as the shared homepage? Why or why not?
๐Ÿ‘๏ธ Show Answer

What to cache: the article and the headline list are shared, read-heavy, and change infrequently โ€” ideal to cache. The per-user recommendations are the riskiest: they're unique per user (low hit ratio) and change often, so a shared cache helps little.

Freshness for a live story: use a short TTL (say 30โ€“60 seconds) so edits appear quickly, and explicitly invalidate the article key each time an editor saves. TTL bounds the worst case; invalidation makes the common case instant.

The million-reader spike: that's a cache stampede โ€” one expired hot key causing a flood of simultaneous misses. Prevent it by letting only one request rebuild the entry while others briefly serve the old value (or wait), and by staggering TTLs so keys don't all expire together.

Personalised feed: not the same way. Cache it per user with a short TTL (or cache the expensive building blocks, like the candidate article set, rather than the final feed). A shared cache key can't serve different users, so the shared-page strategy doesn't apply.

1.14 โ˜๏ธ Cloud Service Mapping

You rarely run a cache server yourself โ€” every cloud offers managed in-memory caches, letting you spin up Redis or Memcached and connect in minutes:

NeedAWS (Primary)GCPAzure
Managed Redis cache โ€” the common defaultElastiCache for RedisMemorystore for RedisAzure Cache for Redis
Managed Memcached cache โ€” simple key-value cachingElastiCache for MemcachedMemorystore for MemcachedAzure Cache for Redis (Redis-based)
Simplest AWS picture: put ElastiCache for Redis between your app servers and the database, read with cache-aside, set a TTL that matches your staleness tolerance, and invalidate keys when data changes. Most reads now hit Redis in microseconds and never reach the database. (Delivering static files close to users is the CDN's job, covered later in this post.)

๐Ÿ”ด 2. Redis Deep Dive

You've seen that a cache is a fast in-memory copy of data โ€” and that the tool almost everyone reaches for to build one is Redis. It's worth understanding on its own, because Redis shows up far beyond caching: as a session store, a rate limiter, a leaderboard, a message broker, and a distributed lock. In this section you will learn what Redis is, how it stores and serves data, exactly why it's so blazingly fast (in-memory storage, a single-threaded event loop, and O(1) hash-table lookups), how much faster it actually is, the other in-memory stores in its family, and when Redis is โ€” and isn't โ€” the right choice.

Redis โ€” an in-memory data structure store and why it is so fast

2.1 ๐ŸŽฏ Introduction

When ShopEasy needs to remember a shopper's cart, count live views on a flash sale, or serve a cached product in under a millisecond, it doesn't reach for its PostgreSQL database โ€” it reaches for Redis. Redis is so fast and so flexible that it has become the default answer to "we need this right now, not in 10 milliseconds."

Redis (the name is short for REmote DIctionary Server) is an in-memory data structure store. Break that phrase down: in-memory means it keeps all its data in RAM rather than on disk, so reads and writes are astonishingly fast; data structure store means it's not just a plain key-value box โ€” it natively understands strings, hashes, lists, sets, sorted sets, and more, each with its own commands. You talk to it with simple commands like SET, GET, and INCR, and it answers in microseconds.

Although it's most famous as a cache, Redis is really a general-purpose in-memory toolkit. The same speed that makes it a great cache makes it ideal for session storage, rate limiting, real-time leaderboards, job queues, pub/sub messaging, and distributed locks. This section treats Redis as a topic in its own right โ€” how it works and why it's fast โ€” rather than just "the thing you cache with."

๐Ÿ’ก A dictionary that lives in RAM. At its core Redis is a giant dictionary (hash table) of keys to values, kept entirely in memory. That one design choice โ€” data in RAM, accessed by key โ€” is the source of both its blazing speed and its main limitation (memory is smaller and more expensive than disk).

2.2 ๐Ÿ’ก Why It Matters

Redis matters because it's everywhere and because it's fast enough to change how you design a system. It has topped the ranking as the most-loved database in developer surveys for years, and it powers the real-time layer of companies like Twitter (timelines), GitHub (background jobs), Stack Overflow, and Snapchat. A single Redis node routinely serves over 100,000 operations per second โ€” tuned setups reach a million or more โ€” at sub-millisecond latency.

Put that speed next to a disk-based database and the gap is enormous:

StoreTypical read latencyRough throughput (per node)
Redis (in-memory)~0.1โ€“1 ms (often under 0.5 ms)100kโ€“1M+ ops/sec
Relational DB (SSD)~1โ€“10 ms per queryThousands of complex queries/sec
Spinning disk read~5โ€“10 ms per seekHundreds of ops/sec
  • Order-of-magnitude speed โ€” Redis is typically 10โ€“100ร— faster than querying a disk-based database, which is why it fronts so many of them as a cache.
  • Simplifies hard problems โ€” atomic counters, TTLs, and sorted sets turn tricky tasks (rate limiting, leaderboards, expiring sessions) into a couple of commands.
  • Predictable latency โ€” because everything is in RAM and operations are simple, response times stay flat and fast even under heavy load.
  • One tool, many jobs โ€” cache, session store, queue, lock, and pub/sub in a single well-understood system, reducing moving parts.
Why system design cares: knowing Redis's speed and data structures lets you answer "how do we make this real-time?" concretely โ€” a leaderboard is a sorted set, a rate limiter is an atomic counter with a TTL, a session is a hash with an expiry. Redis turns many system-design sub-problems into a one-liner.

2.3 ๐Ÿ  Real-world Analogy

Imagine a busy reception desk. Behind it is a vast archive room in the basement โ€” every document the company has ever produced, perfectly organised but a slow elevator ride away. That archive is a disk-based database. On the receptionist's desk sits a ring of labelled index cards holding the answers people ask for all day: today's meeting rooms, extension numbers, the WiFi password. Grabbing a card is instant โ€” no trip downstairs. That ring of cards is Redis.

Three details make the desk fast, and they map exactly onto why Redis is fast. First, the cards are right there on the desk, not in the basement (in memory, not on disk). Second, each card has a clear label you flip straight to โ€” no reading through a filing cabinet (a hash table with direct key lookup). Third, one receptionist handles the cards, so there's never confusion about two people scribbling on the same card at once (a single thread, so no locking overhead).

Reception Desk WorldRedis WorldMeaning
๐Ÿ—„๏ธ Slow basement archive๐Ÿ’ฝ Disk-based databaseEverything, but slower to reach
๐ŸŽด Ring of index cards on the deskโšก Redis (in memory)Hot answers, instant to grab
๐Ÿท๏ธ Flipping to a labelled card๐Ÿ”‘ Key lookup in a hash tableDirect O(1) access โ€” no searching
๐Ÿง‘ One receptionist, no arguments๐Ÿงต Single-threaded executionNo locking; commands run one at a time
๐Ÿ“ธ Occasionally photocopying the cards๐Ÿ’พ Persistence (RDB/AOF) to diskA backup so cards survive a power cut

Notice the trade the desk makes: it can only hold the cards that fit on the desk (memory is limited), and if the building loses power the cards could scatter unless they were photocopied (persistence). Those are exactly Redis's constraints โ€” but for the hot data people ask for constantly, nothing beats a card that's already in your hand.

2.4 ๐Ÿ“– Key Terms

TermSimple DefinitionQuick Example
In-memoryData is kept in RAM, not on diskReads in microseconds
Data structure storeStores typed structures, not just stringsHashes, lists, sets, sorted sets
KeyThe unique name a value is stored undercart:user:42
Hash tableThe structure giving O(1) key lookupJump straight to a key, no scan
Single-threadedOne thread runs commands one at a timeNo locks; commands are atomic
Event loopHandles many connections without blockingI/O multiplexing (epoll)
TTLExpiry time after which a key is removedEXPIRE key 60
PersistenceSaving in-memory data to disk for durabilityRDB snapshot, AOF log
RDBPoint-in-time snapshot of the datasetSave every 5 minutes
AOFAppend-only log of every write commandReplay to rebuild state
ReplicationCopies (replicas) of the data for reads/failover1 primary + 2 replicas
Redis ClusterSharding data across multiple Redis nodesScale beyond one machine's RAM
PipeliningSending many commands in one round tripBatch 100 SETs at once

2.5 ๐Ÿ”ข How It Works

Redis's design is a stack of deliberate choices that all point at one goal: answer as fast as physically possible. Three of them explain its speed (in-memory storage, a single-threaded event loop, and hash-table data structures), and two more make it durable and scalable (persistence and replication/clustering).

A. In-Memory Storage โ€” The Biggest Reason It's Fast

The single biggest reason Redis is fast is that all its data lives in RAM, never touching disk on the read path. The hardware gap is staggering: reading from RAM takes roughly 100 nanoseconds, while even a fast SSD takes around 100 microseconds (~1,000ร— slower) and a spinning disk seek is ~10 milliseconds (~100,000ร— slower). A traditional database must often go to disk; Redis simply never does for a normal read.

This is also Redis's main constraint: RAM is smaller and more expensive than disk, so your entire dataset must fit in memory (or be sharded across nodes). Redis is for your hot, valuable data โ€” not for terabytes of cold records.

B. Single-Threaded Event Loop โ€” Fast Because It's Simple

Surprisingly, Redis processes commands on a single thread โ€” one command at a time. That sounds like it would be slow, but it's a key reason it's fast and predictable:

  • No locks, no contention โ€” multi-threaded databases spend real effort coordinating threads (locks, latches) so they don't corrupt shared data. With one thread, none of that overhead exists.
  • Every command is atomic โ€” because commands run one at a time to completion, an INCR can never interleave with another. Correctness under concurrency comes for free.
  • Each operation is tiny โ€” an in-memory hash lookup takes well under a microsecond, so one thread can still handle 100k+ commands per second.

To serve thousands of connected clients with one thread, Redis uses an event loop with I/O multiplexing (epoll/kqueue): instead of blocking on any one connection, it asks the OS "which connections have data ready?" and processes them as they come. The thread never sits idle waiting on the network. (Modern Redis does use extra threads for a few side tasks like network I/O and background saving, but command execution stays effectively single-threaded.)

C. Hash Table & Optimized Data Structures

At its heart Redis is one big hash table mapping keys to values. A hash table gives O(1) average lookup โ€” to find a key, Redis hashes it and jumps straight to the slot, with no scanning regardless of how many keys exist. That's why a GET is roughly as fast with 10 keys as with 10 million.

Each value is itself a purpose-built, memory-efficient structure with its own fast operations, so you push work into Redis instead of pulling data out to process it:

# String โ€” a cached value or counter SET session:42 "logged_in" EX 3600 # value with a 1-hour TTL INCR views:product:42 # atomic counter # Hash โ€” an object with fields (a shopping cart, a user profile) HSET cart:42 item:1 2 item:2 1 # fields โ†’ quantities # List โ€” a queue or recent-activity feed LPUSH feed:42 "viewed UltraBook" # push to the head # Sorted set โ€” a leaderboard, ranked by score ZADD leaderboard 4500 "alice" 4200 "bimal" ZREVRANGE leaderboard 0 9 # top 10 instantly

A leaderboard's "give me the top 10 by score" is a single ZREVRANGE against a sorted set โ€” no sorting a million rows in your app. The right data structure turns an expensive operation into an instant one.

D. Persistence โ€” Surviving a Restart

Data in RAM vanishes when the process stops, so Redis offers optional persistence to disk โ€” the "photocopy the index cards" step from the analogy. There are two mechanisms, often used together:

  • RDB (snapshots) โ€” periodically dumps the whole dataset to a compact file (e.g. every 5 minutes). Fast to load and small, but a crash can lose everything since the last snapshot.
  • AOF (append-only file) โ€” logs every write command as it happens, so it can be replayed to rebuild the exact state. More durable (down to ~1 second of loss), but larger and slower to replay.

Persistence writes happen in the background, off the command path, so they don't slow down reads and writes. Note the trade-off: even with AOF, Redis is not as bulletproof as a purpose-built database with full ACID durability โ€” which is why Redis is usually a fast layer in front of a durable database, not the permanent home for critical data.

E. Replication & Redis Cluster โ€” Scaling Out

One Redis node is limited by one machine's RAM and one thread. Two techniques scale it, reusing the exact ideas from the scalability post:

  • Replication โ€” a primary node streams its data to one or more replicas. Replicas serve reads (scaling read throughput) and stand ready to be promoted if the primary fails (high availability).
  • Redis Cluster โ€” shards the keyspace across many primaries, each owning a slice of the data (via hash slots). This scales both memory and write throughput beyond a single machine โ€” the sharding pattern applied to Redis.
๐Ÿ“Œ Why Redis is fast, in one line: data in RAM (no disk), reached by hash-table key lookup (no scanning), executed on a single thread (no locking) using purpose-built data structures (no wasted work). Strip away disk, search, and coordination and what's left is close to the hardware's raw speed.

2.6 ๐Ÿ”€ Types & Variations

Redis isn't the only in-memory store. Its success spawned a family of alternatives โ€” some simpler, some faster, some drop-in compatible. Knowing the landscape helps you justify the choice.

๐Ÿงฉ

Memcached

The classic in-memory cache. Multi-threaded, pure string key-value, no persistence or rich types. Simple and fast for plain caching.

๐Ÿ”ต

Valkey

An open-source fork of Redis (created after Redis's 2024 license change), backed by the Linux Foundation. Drop-in compatible and increasingly the default in clouds.

๐Ÿ‰

DragonflyDB

A modern, multi-threaded, Redis-compatible store that scales vertically on one big machine โ€” far higher throughput per node than single-threaded Redis.

โšก

KeyDB

A multi-threaded fork of Redis that keeps the same API but uses multiple cores, boosting throughput without changing your code.

๐ŸŒ

Hazelcast

A distributed in-memory data grid from the Java world, built for clustering and in-memory compute across many nodes.

๐Ÿš€

Aerospike

A key-value store optimised for a hybrid of RAM and flash (SSD), targeting huge datasets at low latency โ€” larger-than-memory workloads.

The pattern: most alternatives either simplify Redis (Memcached), stay API-compatible while adding threads (KeyDB, DragonflyDB, Valkey), or push past RAM limits (Aerospike). Redis remains the default because of its maturity, rich data structures, and ecosystem โ€” but "use more CPU cores" or "exceed memory" are the usual reasons to look elsewhere.

2.7 ๐ŸŽจ Illustrated Diagram

The diagram shows why Redis is fast. Many clients connect through one event loop that feeds a single command-execution thread, which reads and writes an in-memory hash table. Persistence to disk happens in the background, off the fast path.

%%{init: {"theme": "base", "themeVariables": {"lineColor": "#64748b", "edgeLabelBackground": "#fff"}}}%% flowchart TD C1["๐Ÿ“ฑ Client"] C2["๐Ÿ“ฑ Client"] C3["๐Ÿ“ฑ Client"] EL["๐Ÿ” Event Loop\n(I/O multiplexing)"] T["๐Ÿงต Single Thread\n(commands run atomically)"] M["โšก In-Memory Hash Table\n(O(1) key lookup)"] D["๐Ÿ’พ Disk (RDB / AOF)\nbackground only"] C1 --> EL C2 --> EL C3 --> EL EL --> T T --> M M -. "async save" .-> D style C1 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a style C2 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a style C3 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a style EL fill:#f5f3ff,stroke:#7c3aed,color:#4c1d95 style T fill:#d1fae5,stroke:#059669,color:#064e3b style M fill:#fff3e0,stroke:#d97706,color:#92400e style D fill:#fce4ec,stroke:#e53935,color:#b71c1c

Reading the diagram: the event loop juggles thousands of connections without blocking, the single thread runs each command to completion (so no locks are needed), and every operation touches only fast in-memory structures. Disk is reached only by a background save โ€” never on the read path โ€” so client latency stays in the microsecond range.

2.8 โœ… When to Use

Redis is the right tool when you need speed and your data fits in memory. It's the wrong tool as a primary store for large or must-be-durable datasets. The rule: Redis is a fast layer, not usually the source of truth.

Use Redis whenโ€ฆDon't use Redis whenโ€ฆ
You need sub-millisecond reads/writes on hot dataIt's your only copy of critical, must-never-lose data
The working set fits in RAM (or shards across nodes)The dataset is huge and mostly cold (terabytes on disk)
Access is by key, or fits a Redis structureYou need complex multi-table joins or ad-hoc queries
You want ready-made counters, TTLs, or sorted setsYou need full ACID transactions across many records
You need pub/sub, locks, or a fast queueStrong long-term durability is the top priority

Where Redis shines โ€” its greatest-hits use cases:

Use caseWhy Redis fits
CachingSub-ms reads with built-in TTL and eviction โ€” the classic job
Session storeFast, shared, expiring per-user state that keeps app servers stateless
Rate limitingAtomic INCR with a TTL counts requests per window correctly
LeaderboardsSorted sets rank millions of scores and return the top N instantly
Queues & pub/subLists and streams back job queues; pub/sub powers real-time messaging
Distributed locksAtomic set-if-not-exists coordinates work across many servers
Rule of thumb: use Redis as a fast layer in front of a durable database, not as a replacement for it. Keep the source of truth in your SQL/NoSQL store; put the hot, speed-critical, or real-time pieces โ€” cache, sessions, counters, leaderboards โ€” in Redis.

2.9 ๐Ÿ—๏ธ Real-world Example โ€” One Redis, Five Jobs at ShopEasy

During a flash sale, a single Redis deployment quietly does five different jobs for ShopEasy โ€” each using a different data structure, each answered in well under a millisecond:

JobRedis structure & commandWhat it does
โšก CacheString โ€” GET product:42Serves the hot product page without touching PostgreSQL
๐Ÿ”‘ SessionHash โ€” HGETALL session:abc (+ TTL)Holds the logged-in user's state so any app server can serve them
๐Ÿ›’ CartHash โ€” HSET cart:42 item:1 2Tracks cart items and quantities with instant updates
๐Ÿšฆ Rate limitString counter โ€” INCR + EXPIREBlocks bots by counting requests per user per minute
๐Ÿ† LeaderboardSorted set โ€” ZREVRANGE top 0 9Shows the top-selling products in real time
The payoff: one well-understood system covers caching, sessions, carts, rate limiting, and real-time ranking โ€” jobs that would otherwise need several specialised tools. The durable record (orders, payments) still lives in the database; Redis handles everything that must be fast. This is why Redis is so often the first thing added after the database itself.

2.10 โš–๏ธ Trade-offs

Redis's speed comes from living in memory and running on one thread โ€” the same choices that create its limits. The trade-offs:

โœ… AdvantagesโŒ Disadvantages
Blazing speed โ€” sub-ms latency, 100kโ€“1M+ ops/secRAM-bound โ€” dataset must fit in memory, which is costly at scale
Rich data structures โ€” hashes, sorted sets, streams, and moreDurability limits โ€” can lose recent writes on a crash vs a full ACID DB
Atomic operations โ€” correct counters and locks with no locking codeNo complex queries โ€” no joins or rich ad-hoc querying like SQL
Versatile โ€” cache, sessions, queues, pub/sub, locks in one toolSingle-threaded ceiling โ€” one node uses one core for commands
Simple & mature โ€” easy commands, huge ecosystem, battle-testedMemory management care โ€” needs eviction/TTL tuning to avoid filling up
๐Ÿ“Œ The core tension: Redis trades capacity and durability for raw speed. That's exactly the right trade for hot, transient, or real-time data โ€” and exactly the wrong one for your permanent, must-never-lose system of record. Use it for what it's built for and pair it with a durable database.

2.11 ๐Ÿšซ Common Mistakes

#โŒ Common Mistakeโœ… The Reality
1 Using Redis as the primary database Redis can lose recent writes on a crash and is RAM-bound. Keep the source of truth in a durable DB; use Redis as a fast layer in front.
2 No memory limit or eviction policy Left unbounded, Redis fills RAM and crashes or starts swapping. Set maxmemory and an eviction policy from day one.
3 Forgetting TTLs on transient keys Sessions and cache entries without expiry pile up forever. Set a TTL so temporary data cleans itself up.
4 Running slow commands on big keys Commands like KEYS * or sorting a huge set block the single thread and stall everyone. Avoid O(N) commands on large data in production.
5 One round trip per command Thousands of separate calls waste network time. Use pipelining or Lua scripts to batch many operations into one round trip.
6 Assuming Redis is single-node forever One node is a memory and availability limit. Plan for replication (reads/failover) and Redis Cluster (sharding) before you hit the ceiling.

2.12 ๐Ÿ“ Summary

  • Redis is an in-memory data structure store โ€” a RAM-resident dictionary of keys to rich values (strings, hashes, lists, sets, sorted sets).
  • It's fast for three reasons โ€” data in RAM (no disk), O(1) hash-table lookups (no scanning), and a single thread (no locking) over purpose-built structures.
  • 10โ€“100ร— faster than a disk DB โ€” sub-millisecond latency and 100kโ€“1M+ ops/sec per node.
  • Durability and scale are add-ons โ€” RDB/AOF persistence, replicas for reads/failover, and Redis Cluster for sharding.
  • A fast layer, not the source of truth โ€” ideal for caching, sessions, counters, leaderboards, queues, and locks in front of a durable database.

2.13 ๐Ÿ‹๏ธ Design Challenge

๐ŸŽฎ Challenge: Use Redis in a live multiplayer game

You're building a real-time multiplayer game. You need a live global leaderboard, per-player sessions, protection against players spamming actions, and a way to broadcast events to all servers. Think through the following:
  • Which Redis data structure powers the leaderboard, and how do you get the top 10 instantly?
  • Players spam the "attack" button. How do you rate-limit each player with Redis, and why is it correct under concurrency?
  • Where do final match results and player accounts belong โ€” Redis or a durable database? Why?
  • A single Redis node can't hold all players' live state. How do you scale it?
๐Ÿ‘๏ธ Show Answer

Leaderboard: a sorted set keyed by score. ZADD leaderboard <score> <player> on each update, and ZREVRANGE leaderboard 0 9 returns the top 10 in one fast call โ€” Redis keeps it ranked for you, no sorting in your app.

Rate limiting: an atomic counter per player per window โ€” INCR rate:player:42 with EXPIRE of, say, 1 second; if the count exceeds the limit, reject. It's correct under concurrency because Redis runs commands on a single thread, so INCR is atomic โ€” no two attacks can race the counter.

Durable data โ†’ database: final match results, player accounts, and purchases go in a durable DB (they must never be lost). Redis holds the live, transient state (current scores, sessions) that's fine to lose on a crash โ€” the source of truth stays in the database.

Scaling: use replication (replicas serve reads and provide failover) and Redis Cluster to shard players' state across multiple nodes, growing past one machine's RAM. Broadcasting events to all servers is a natural fit for Redis pub/sub.

2.14 โ˜๏ธ Cloud Service Mapping

You rarely operate Redis yourself in production โ€” every cloud offers a managed, replicated Redis (or Valkey) you configure and connect to:

NeedAWS (Primary)GCPAzure
Managed Redis / Valkey โ€” cache & in-memory storeElastiCache for Redis / ValkeyMemorystore for RedisAzure Cache for Redis
Durable in-memory store โ€” Redis-compatible, persistentAmazon MemoryDBMemorystore (with persistence)Azure Cache for Redis (persistence tier)
Simplest AWS picture: use ElastiCache for Redis/Valkey as your cache, session store, and rate limiter, with replication turned on for failover. If you need Redis speed and stronger durability as a primary store, MemoryDB is the Redis-compatible, durable option. The managed service handles replication, failover, patching, and backups for you.

๐ŸŒ 3. CDN

A cache makes repeat data fast, but it can't beat physics: if your only server is in Virginia, a shopper in Tokyo still waits for data to cross the planet and back. A Content Delivery Network (CDN) solves that by keeping copies of your static content on edge servers in hundreds of cities worldwide, so each user is served from a location physically close to them. In this section you will learn what a CDN is and what an edge server and origin server are, how a request is routed to the nearest edge, how content gets cached and refreshed at the edge, what to serve from a CDN (and what not to), and how CDN cache invalidation works.

CDN โ€” edge servers delivering static content from locations close to users worldwide

3.1 ๐ŸŽฏ Introduction

ShopEasy's servers live in a data centre in Virginia, USA. A shopper in Virginia loads a product page in a snap. But a shopper in Tokyo? Every image on that page has to travel from Virginia to Japan and back โ€” roughly 15,000 kilometres each way. Even at the speed of light through fibre, that round trip adds hundreds of milliseconds, and a page full of images feels sluggish. The data is fine; the distance is the problem.

A Content Delivery Network (CDN) solves the distance problem. It's a globally distributed network of servers โ€” called edge servers or points of presence (PoPs) โ€” placed in hundreds of cities around the world. The CDN keeps copies of your static content (images, CSS, JavaScript, videos) on these edge servers. When the Tokyo shopper requests an image, it's served from an edge server in Tokyo โ€” a few milliseconds away โ€” instead of from Virginia.

Two terms anchor everything in this section. The origin server is your own server (or storage) that holds the original, authoritative copy of the content. The edge server is a CDN machine near the user that holds a cached copy. In fact, a CDN is really caching applied to geography โ€” the same hit/miss/TTL ideas from the Caching sub-topic, now spread across the planet and focused on static files.

๐Ÿ’ก A CDN is a geographic cache. Caching keeps data in memory to avoid a slow database; a CDN caches files on nearby servers to avoid a slow, long-distance network trip. Same core idea โ€” keep a copy close to where it's needed โ€” applied to physical distance.

3.2 ๐Ÿ’ก Why It Matters

For any application with users beyond a single city, a CDN is close to mandatory. The scale is staggering: Netflix delivers over 15% of the world's downstream internet traffic, almost entirely through its own CDN of edge servers placed inside ISP networks; Cloudflare and Akamai operate hundreds of points of presence serving trillions of requests a day. A CDN routinely cuts image and video load times from hundreds of milliseconds to single digits for far-away users.

  • Latency โ€” content is served from a server near the user, so distance stops dominating load time. This is the headline benefit.
  • Offloads the origin โ€” the CDN answers the vast majority of static requests, so your origin servers and bandwidth bill shrink dramatically.
  • Scale & spikes โ€” a global edge network absorbs traffic surges (a viral video, a product launch) that would flatten a single origin.
  • Reliability & security โ€” if the origin is briefly down, edges can still serve cached content; CDNs also absorb DDoS attacks and terminate TLS at the edge.
Why system design cares: in almost every design with a global audience or heavy media (photos, video, downloads), the answer to "how do users far away get this fast?" is a CDN. It pairs naturally with object storage โ€” store the file once, let the CDN deliver it everywhere โ€” and it's one of the cheapest, highest-impact additions you can make.

3.3 ๐Ÿ  Real-world Analogy

Think of a global coffee chain with one central roastery that produces all its beans. If every customer had to order directly from the roastery and wait for shipping, coffee would be slow and expensive. Instead, the chain opens local branches in every city, each stocked with beans from the roastery. You walk into the branch near you and get your coffee in minutes. The roastery is the origin server; the local branches are the edge servers.

Each branch keeps a stock of the popular blends (cached content). If you order something a branch has, you're served instantly (an edge hit). If you order a rare blend the branch doesn't stock, it requests a batch from the roastery, serves you, and keeps some on hand for the next customer (an edge miss, then cached). Branches restock periodically so their beans don't go stale (TTL), and when the roastery changes a recipe, it tells the branches to clear the old stock (invalidation / purge).

Coffee Chain WorldCDN WorldMeaning
๐Ÿญ The central roastery๐Ÿ—„๏ธ Origin serverHolds the original, authoritative content
๐Ÿช Local branch near you๐ŸŒ Edge server (PoP)A nearby copy that serves you fast
โ˜• Ordering a stocked blendโœ… Edge cache hitServed locally, instantly
๐Ÿ“ฆ Branch fetching a rare blendโŒ Edge cache missPulled from origin, then cached at edge
๐Ÿ” Periodic restockingโณ TTL refreshEdge re-checks origin after expiry
๐Ÿงน "Clear the old recipe" order๐Ÿงฝ Purge / invalidationForce edges to drop outdated content

The chain's genius is that customers never feel the distance to the roastery โ€” the branch nearby handles almost everything, and the roastery only makes what the branches can't. A CDN gives your content that same network of always-nearby branches.

3.4 ๐Ÿ“– Key Terms

TermSimple DefinitionQuick Example
CDNA global network of servers that deliver content close to usersCloudFront, Cloudflare, Akamai
Origin ServerYour server/storage holding the original contentAn S3 bucket or your web server
Edge ServerA CDN server near users that caches contentA PoP in Tokyo
PoP (Point of Presence)A physical CDN location, usually with many edge servers300+ PoPs worldwide
Edge Cache HitContent found on the nearby edge serverImage served from the local PoP
Edge Cache MissEdge lacks it, so it fetches from originFirst request in a region
Static ContentFiles that don't change per requestImages, CSS, JS, videos, PDFs
Dynamic ContentContent generated per user/requestA personalised account page
TTLHow long an edge keeps content before re-checking originCache an image for 24 hours
Purge / InvalidationForcing edges to drop cached content earlyClear a logo after a redesign
Cache-ControlHTTP header telling the CDN how to cache a responsemax-age=86400

3.5 ๐Ÿ”ข How It Works

Let us break a CDN into four pieces: the origin-and-edge setup, how a request is routed to the nearest edge, how the edge serves content (hit vs miss, pull vs push), and how content stays fresh through TTL and purging.

A. Origin & Edge

You keep the authoritative copy of your files on the origin โ€” typically object storage (like S3) or your web server. You then put a CDN in front of it. Each of the CDN's hundreds of edge servers starts empty and fills up on demand with copies of the files users actually request in that region. The origin is written once; the edges are read many times, everywhere.

Crucially, you don't change your app much. You just serve your static assets from the CDN's domain (or point your domain at the CDN), and the CDN handles fetching from origin, caching at the edge, and routing users โ€” all transparently.

The two roles have very different jobs, and telling them apart is the heart of this section:

AspectOrigin ServerEdge Server
RoleHolds the original, authoritative contentHolds a cached copy near users
How manyOne (or a few, in a couple of regions)Hundreds of PoPs worldwide
Owned byYou (your servers or object storage)The CDN provider
What it storesEverything โ€” the full set of filesOnly the hot subset requested in that region
Traffic it seesOnly misses and dynamic requestsThe vast majority of user requests
Distance to userPossibly far (one location)Close โ€” a few milliseconds away

An origin can be almost anything that serves files: an object-storage bucket (S3, the most common), a web/application server, or a custom origin elsewhere. To protect it, large CDNs add tiered caching (also called origin shielding): edges don't each hit the origin directly on a miss โ€” they fall back to a smaller set of regional "shield" caches, which hit the origin only if they too miss. So even if a file is missing from 300 edges, the origin is fetched from just once or twice, not 300 times. This keeps the origin's load tiny no matter how many edges exist.

B. Routing to the Nearest Edge

The magic is that a user automatically reaches a nearby edge without doing anything special. CDNs achieve this two common ways:

  • Anycast โ€” many edge servers share the same IP address, and internet routing naturally delivers each user to the closest one. The user connects to "the" address; the network picks the nearest physical machine.
  • DNS-based geo-routing โ€” when the user's browser looks up the CDN hostname, the CDN's DNS returns the IP of the edge nearest to them, based on their location.

Either way, the shopper in Tokyo is transparently connected to a Tokyo edge, and the shopper in London to a London edge โ€” from the same URL. "Nearest" is usually measured by network distance (latency), not strictly geography.

C. Edge Hit, Edge Miss, Pull vs Push

Once a request reaches an edge, it's the same hit/miss logic as any cache โ€” just geographic. On an edge hit, the file is already on that edge and returns immediately. On an edge miss, the edge fetches the file from the origin (once), serves it to the user, and stores it locally so the next request in that region is a hit.

How content first gets to the edge comes in two flavours:

  • Pull CDN โ€” edges fetch from origin lazily, on the first request (a miss). You just publish to origin; the CDN pulls as needed. Simplest and most common.
  • Push CDN โ€” you upload content to the CDN ahead of time. Good for large files you know will be requested (a new video release), avoiding a slow first-request miss.

D. Freshness โ€” TTL, Cache-Control & Purging

Edges must know how long to keep a copy before checking the origin again. That's controlled by the Cache-Control HTTP header the origin sends with each file โ€” its max-age sets the edge TTL:

# Origin response header โ€” cache this image at the edge for 24 hours Cache-Control: public, max-age=86400 # A file that should never be cached (e.g. a personalised response) Cache-Control: no-store

When you change a file before its TTL expires โ€” say you fix a logo โ€” edges would keep serving the old one until it ages out. Two techniques fix this: purging (explicitly telling the CDN to drop a file from all edges immediately) and cache busting (changing the file's URL, e.g. logo.v2.png or app.abc123.js, so it's treated as a brand-new object). Cache busting is the standard for versioned assets because it sidesteps invalidation entirely.

๐Ÿ“Œ The freshness trick: give static assets a very long TTL and a versioned filename. Long TTL = maximum cache hits; a new filename on every change = instant "invalidation" with no purge needed. This is why build tools output files like main.9f2a1c.js.

3.6 ๐Ÿ”€ Types & Variations

CDNs started as simple static-file delivery but have grown into a broad edge platform. These are the main jobs a modern CDN does.

๐Ÿ–ผ๏ธ

Static Delivery

The classic job โ€” caching images, CSS, JS, fonts, and downloads at the edge. Highest hit ratios and the biggest, easiest win.

๐ŸŽฌ

Media Streaming

Delivering video and audio in chunks from nearby edges, so playback starts fast and doesn't buffer. Powers Netflix, YouTube-scale streaming.

โš™๏ธ

Dynamic & Edge Compute

Speeding up non-cacheable requests via optimized routing, and running small functions at the edge (Cloudflare Workers, Lambda@Edge).

๐Ÿ›ก๏ธ

Security

Absorbing DDoS attacks, filtering traffic with a WAF, and terminating TLS at the edge โ€” all before traffic reaches your origin.

Pull vs Push. Beyond what they deliver, CDNs differ in how content arrives: a pull CDN fetches from origin on the first miss (simplest, best for many files), while a push CDN has you upload content ahead of time (best for a few large, known files like a new video release).

3.7 ๐ŸŽจ Illustrated Diagram

The diagram shows users worldwide reaching their nearest edge server. Most requests are edge hits served locally; only a cache miss travels back to the single origin, which then fills that edge for everyone in the region.

%%{init: {"theme": "base", "themeVariables": {"lineColor": "#64748b", "edgeLabelBackground": "#fff"}}}%% flowchart TD U1["๐Ÿ—ผ User (Tokyo)"] U2["๐Ÿ‡ฌ๐Ÿ‡ง User (London)"] E1["๐ŸŒ Edge (Tokyo)"] E2["๐ŸŒ Edge (London)"] O["๐Ÿ—„๏ธ Origin (Virginia)"] U1 -->|"request"| E1 U2 -->|"request"| E2 E1 -->|"โœ… hit โ†’ serve local"| U1 E2 -->|"โŒ miss โ†’ fetch"| O O -->|"file"| E2 E2 -->|"serve + cache"| U2 style U1 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a style U2 fill:#dbeafe,stroke:#2563eb,color:#1e3a8a style E1 fill:#d1fae5,stroke:#059669,color:#064e3b style E2 fill:#d1fae5,stroke:#059669,color:#064e3b style O fill:#fff3e0,stroke:#d97706,color:#92400e

Reading the diagram: the Tokyo user gets an instant edge hit โ€” no trip to Virginia. The London edge is missing the file, so it fetches from origin once, then serves and caches it, so the next London user gets a hit too. The origin's job shrinks to filling edges on misses, not serving every user directly.

3.8 โœ… When to Use

A CDN is ideal for static content served to a geographically spread audience. It's the wrong tool for content that's personalised per request or that must be perfectly live.

Use a CDN whenโ€ฆSkip / limit the CDN whenโ€ฆ
You serve static assets (images, CSS, JS, video, downloads)Content is unique per request (personalised dashboards)
Users are spread across regions or countriesAll users are in one location near your origin
Traffic is high or spiky and you want to offload the originData must be real-time exact (live trading prices)
Content changes rarely, or you can version its URLContent changes every second and can't be versioned
You want DDoS protection and edge TLS for freeTruly private data that shouldn't sit on shared edges

A quick guide to what belongs on a CDN:

ContentCDN?
Product images, logos, thumbnailsโœ… Yes โ€” static, shared, huge win
CSS, JavaScript bundles, fontsโœ… Yes โ€” version the filename for easy busting
Videos and large downloadsโœ… Yes โ€” offloads massive bandwidth
A logged-in user's personalised feedโš ๏ธ Usually no โ€” cache building blocks instead
A live stock ticker or bank balanceโŒ No โ€” must be current, per-request
Rule of thumb: put every static asset behind a CDN with a long TTL and versioned filenames โ€” it's cheap and almost always helps. Leave dynamic, per-user, or must-be-live responses on the origin (or use edge compute for the parts that can be sped up). Store the file once in object storage, let the CDN deliver it everywhere.

3.9 ๐Ÿ—๏ธ Real-world Example โ€” A Tokyo Shopper Loads ShopEasy

ShopEasy's origin is in Virginia, but a shopper in Tokyo opens a product page. Follow how the CDN delivers a fast page without dragging every file across the Pacific:

StepActorWhat Happens
โ‘ ๐Ÿ—ผ Shopper (Tokyo)Requests the page; static asset URLs point at the CDN
โ‘ก๐Ÿงญ CDN routingAnycast/DNS sends her to the Tokyo edge, a few ms away
โ‘ข๐ŸŒ Tokyo edgeLogo, CSS, and JS are already cached โ†’ instant edge hits
โ‘ฃ๐Ÿ–ผ๏ธ Tokyo edgeThe product image is a miss (first request in Japan) โ†’ fetch from Virginia origin once
โ‘ค๐Ÿ—„๏ธ Origin (Virginia)Sends the image with Cache-Control: max-age=86400; the edge caches it for 24h
โ‘ฅ๐Ÿ‘ฅ Next Tokyo shoppersAll get the image as an edge hit โ€” the origin is never touched again for a day
โ‘ฆโš™๏ธ App server + cacheThe dynamic bits (live price, stock) still come from the origin, sped up by the Redis cache from Sub-topic 2
The payoff: nearly the whole page loads from a server in Tokyo instead of Virginia โ€” dropping load time from hundreds of milliseconds to single digits. The CDN handles the static bulk; the origin (with its cache) handles only the small dynamic part. Caching and CDN work together: one keeps data fast, the other keeps distance short.

3.10 โš–๏ธ Trade-offs

A CDN is one of the highest-value, lowest-effort upgrades for a global app โ€” but it adds another cached layer with its own staleness and cost considerations:

โœ… AdvantagesโŒ Disadvantages
Low latency worldwide โ€” content served from near the userStale content risk โ€” edges can serve old files until TTL/purge
Origin offload โ€” most static traffic never reaches your serversInvalidation delay โ€” purging across hundreds of edges isn't instant
Absorbs spikes & DDoS โ€” the edge network takes the hitCost โ€” CDN bandwidth is a real (though usually worthwhile) bill
Bandwidth savings โ€” cheaper egress than serving from originLess useful for dynamic data โ€” personalised/live content can't be edge-cached
Reliability โ€” edges can serve cached content if origin is downDebugging complexity โ€” "which edge served this, and was it stale?"
๐Ÿ“Œ Same tension as caching: a CDN trades freshness for speed and reach, just across geography instead of memory. Manage it the same way โ€” long TTLs for things that rarely change, versioned URLs for instant updates, and never edge-cache truly dynamic or private data.

3.11 ๐Ÿšซ Common Mistakes

#โŒ Common Mistakeโœ… The Reality
1 Putting dynamic, per-user content on the CDN Edge-caching a personalised page can serve one user's data to another. Only cache shared, static content; mark private responses no-store.
2 No cache-busting on updated assets Reusing style.css after a change leaves users on the old cached file until TTL expires. Version the filename (style.abc123.css).
3 Setting TTLs too short Tiny TTLs cause constant edge misses back to origin, defeating the CDN. Use long TTLs plus versioned URLs instead.
4 Relying on purge for every change Purging hundreds of edges is slower and rate-limited. Prefer versioned filenames; reserve purge for emergencies.
5 Forgetting the origin still needs protecting Misses and dynamic requests still hit origin. Keep origin scalable and cached; the CDN reduces load, it doesn't remove it.
6 Ignoring Cache-Control headers Without correct headers the CDN may cache too long, too short, or not at all. The origin's headers are how you control edge behaviour.

3.12 ๐Ÿ“ Summary

  • A CDN is a geographic cache โ€” edge servers worldwide hold copies of static content close to users, beating the latency of distance.
  • Origin vs edge โ€” the origin holds the authoritative copy; edges serve cached copies and fetch from origin only on a miss.
  • Users reach the nearest edge automatically โ€” via Anycast or DNS geo-routing, from the same URL.
  • Freshness via TTL + versioned URLs โ€” long TTLs maximise hits; changing the filename gives instant, purge-free updates.
  • Static yes, dynamic no โ€” cache shared static assets at the edge; keep per-user and must-be-live content on the origin.

3.13 ๐Ÿ‹๏ธ Design Challenge

๐ŸŽฅ Challenge: Deliver a global video platform

You're launching a video platform with viewers on every continent. Videos are large; the homepage and thumbnails are shown to everyone; each user also has a private watch history. Think through the following:
  • Which content goes on the CDN, and which stays on the origin?
  • For a brand-new blockbuster release you know will be watched instantly, would you use a pull or push CDN โ€” and why?
  • You re-encode a video and must replace the old file. How do you make every edge serve the new version without a slow global purge?
  • Where does the origin's own cache (from Sub-topic 1) still help, even with a CDN in front?
๐Ÿ‘๏ธ Show Answer

CDN vs origin: videos, thumbnails, the homepage assets, CSS/JS โ†’ CDN (static, shared, huge bandwidth). The private watch history and any personalised recommendations โ†’ origin (per-user, not edge-cacheable), though you can still cache their building blocks in Redis.

Push for the blockbuster: use a push CDN (pre-load the file to edges before launch). You know it'll be requested everywhere immediately, so pre-warming avoids a wave of first-request misses all hammering the origin at release time.

Replacing a video: don't purge โ€” use cache busting. Give the re-encoded file a new URL (e.g. movie_v2.mp4 or a content hash) so every edge treats it as a new object and fetches it fresh, while the old URL harmlessly ages out. Instant, global, no purge storm.

Origin cache still helps: on edge misses and for the dynamic requests the CDN can't cache (watch history, recommendations), the origin's Redis cache keeps those responses fast and shields the database. CDN handles distance; the cache handles repeat work โ€” you want both.

3.14 โ˜๏ธ Cloud Service Mapping

Every major cloud (and dedicated CDN providers) offers a managed global edge network you point your content at:

NeedAWS (Primary)GCPAzure
Managed CDN โ€” global static deliveryAmazon CloudFrontCloud CDNAzure Front Door / CDN
Edge compute โ€” run code at the edgeLambda@Edge / CloudFront FunctionsCloud Functions (edge)Azure Functions (edge)
Simplest AWS picture: store static assets in S3, put CloudFront in front as the CDN, set long Cache-Control TTLs with versioned filenames, and serve dynamic requests from your origin (backed by the ElastiCache Redis cache from Sub-topic 2). Dedicated providers like Cloudflare and Akamai offer the same, often with more edge locations. Distance handled by the CDN, repeat work handled by the cache.

๐Ÿ“š References

  • System Design Interview (Vol. 1) โ€” Alex Xu โ€” Uses caching and a CDN as two of the earliest additions when scaling a web system.
  • Designing Data-Intensive Applications โ€” Martin Kleppmann โ€” Covers caching layers, consistency, and the cost of stale data.
  • Cloudflare Learning Center โ€” Excellent beginner-friendly explanations of CDNs, edge servers, and how content is cached close to users.
  • AWS โ€” Amazon CloudFront & ElastiCache docs โ€” Practical references for running a managed CDN and in-memory cache.
  • Redis Documentation โ€” The canonical guide to in-memory caching, TTLs, and eviction policies.