Serving reads: caching, hot keys and sharding
The primer’s scaled design, how much each cache layer absorbs under skew (λT/(1+λT)), hot keys, and which ways of sharding counts give the right top N.
What you will learn
- Scale reads with the primer's cache, replicas and warehouse.
- Work out how much each cache layer absorbs from a page's request rate and TTL.
- Handle a hot category, and shard rankings without merging partial counts wrongly.
Scaling reads (the primer)
The primer's scaled design:
- A memory cache handles "traffic for popular content (and their sales rank)", which also absorbs uneven traffic and spikes. With so many reads, "the SQL Read Replicas might not be able to handle the cache misses", so further SQL scaling patterns may be needed.
- 400 writes per second on average "might be tough for a single SQL Write Master-Slave". The primer points to federation, sharding, denormalization and SQL tuning, and to moving some data to NoSQL.
- An analytics database (a warehouse such as Redshift or BigQuery) for the computation. Raw data beyond a limited period stays in the object store, which "can comfortably handle" 40 GB a month.
Layers of caching
Rankings change at most hourly in the primer's design, so cached copies stay valid for a long time. You can cache them at several layers:
- CDN or shared HTTP cache: send
Cache-Control: public, max-age=60on the category page (RFC 9111, chapter 9). - In-process cache on each app server, with a short TTL.
- Redis holding the rankings.
- The database with read replicas.
How much does each layer absorb? Suppose a page is requested λ times a second, and each miss caches it for seconds. Then each miss is followed by about hits, so the hit ratio is
Popular categories are requested often, so they nearly always hit. Rarely viewed ones mostly miss. The visual applies this with Zipf-distributed popularity over the primer's 1,000 categories, so you can see how skew, TTLs and the number of app servers change the load that reaches Redis. Every cache layer adds staleness of up to its TTL. With hourly rankings, a TTL of a minute costs little.
Hot keys and sharding
A single very popular category is a hot key: all its reads land on the one Redis node that holds it. Caches in front (above) take most of that load, and Redis replicas can share the reads.
Writes can also concentrate: a purchase updates every category the product belongs to. If one category's updates are too many for one node, split them, but think about what each shard holds:
- Split by product: each product lives in exactly one shard, holding its full count. A category's top is then the top of the merged per-shard top- lists.
- Split writes for the same product across shards: each shard holds only partial counts. Merging the shards' top lists is now wrong. A product could be just outside the top in every shard yet first overall. You must sum each product's counts first (
ZUNIONSTOREdoes this) and then rank. The visual's tests include a case where the shortcut picks the wrong winner.
How much each cache tier absorbs
Reads for 1,000 category pages, with Zipf popularity. A TTL cache serving a key requested λ times a second for T seconds hits λT/(1+λT) of the time; each app server has its own in-process cache.
- Global Shoppers (42k RPS): 1 × Clients & Users
- Route53 Anycast Geo-DNS: 2 × Anycast DNS / Route53
- CloudFront Edge CDN (60s TTL): 6 × CloudFront / CDN Edge
- Application Load Balancer: 8 × Application Load Balancer
- Read API Compute (L1 Cache): 26 × App Compute Server
- Redis ZSET Cluster (L2 Cache): 7 × Redis In-Memory Cache
- Sharded DB Read Replicas (L3): 3 × Database Read Replica
- Global Shoppers (42k RPS) sends requests to Route53 Anycast Geo-DNS
- Route53 Anycast Geo-DNS sends requests to CloudFront Edge CDN (60s TTL)
- Route53 Anycast Geo-DNS sends requests to Application Load Balancer
- CloudFront Edge CDN (60s TTL) sends requests to Application Load Balancer
- Application Load Balancer sends requests to Read API Compute (L1 Cache)
- Read API Compute (L1 Cache) sends requests to Redis ZSET Cluster (L2 Cache)
- Read API Compute (L1 Cache) sends requests to Sharded DB Read Replicas (L3)
Key takeaways
- Rankings change at most hourly, so cache them at the CDN, in each app server, in Redis and in the database.
- A page requested times a second and cached for seconds hits of the time: popular categories nearly always hit.
- A hot category's reads land on one Redis node; the caches in front and Redis replicas share them.
- Splitting by product keeps full counts, so merged top- lists are right; splitting one product's counts across shards makes that merge wrong.
Sources
- System Design Primer: Design Amazon's sales rank, scaling the design
- RFC 9111: HTTP Caching
- Redis: ZUNIONSTORE command
Adapted from The System Design Primer by Donne Martin, licensed under CC BY 4.0. Changes were made.