Taming hot keys and hot partitions
Open in the interactive app: practice, quiz, mock interview →In one minute
- A hot key is a single key (a celebrity profile, a viral post, a flash-sale SKU) that receives a large share of traffic. Partitioning spreads keys, not load, so one key lands on one shard and overwhelms it while the rest of the cluster idles.
- Interviewers raise it whenever your design hashes by an ID that can go viral: social feeds, like counters, leaderboards, ticketing, rate limiters, caches. The probe is usually "what happens when Taylor Swift posts?"
- Read-hot keys are fixed by copying: a local in-process cache, replicas of the key under salted names, or a CDN. Write-hot keys are fixed by splitting: shard the counter into N sub-keys and sum on read, or buffer and batch writes.
- You cannot fix what you cannot see. Detect hot keys with sampled counting (count-min sketch, top-k) per shard and react automatically.
- Every fix trades something: staleness for read replicas, read cost for split counters, memory for local caches. A strong answer names the trade.
How it works
Why hashing does not save you
Consistent hashing or range partitioning gives each shard a fair share of the key space. Real traffic follows a power law, so a handful of keys carry a large fraction of requests: traces from 153 of Twitter's production cache clusters showed approximately Zipfian popularity, sometimes with very high skew (Yang et al., OSDI 2020). If one key gets 200,000 reads per second and a single Redis shard comfortably serves something on the order of 100,000 simple operations per second per core, that shard saturates no matter how many other shards you add. Adding nodes helps the long tail; it does nothing for the head.
Read-hot keys
The goal is to serve one logical key from many physical places.
- Local (L1) cache in the application. Each app server keeps the hottest few thousand values in memory with a short TTL, say 1 to 5 seconds. With 500 app servers, the backing shard sees at most 500 refreshes per TTL window instead of 200,000 requests per second. Staleness is bounded by the TTL. Redis can instead push invalidations to such a cache (server-assisted client-side caching), but a dropped connection flushes the whole local cache, and Redis advises keeping frequently updated keys such as counters off it, since the invalidation traffic can cost more than the cache saves (Redis docs).
- Key replication with salting. Store the value under
post:42#0throughpost:42#7, which hash to different shards. Readers pick a suffix at random. Writers update all copies (or invalidate them). Read capacity grows by the fan-out factor; write cost grows by the same factor. - Push to the edge. For public, cacheable content, a CDN absorbs the load at the edge.
Write-hot keys
Copying does not help writes, because every copy must take every write. Instead:
- Split counters. A like counter becomes
likes:42:0throughlikes:42:15. Each increment picks a random sub-key; a read sums all 16. Writes spread across shards; reads cost 16 lookups, which you then cache. - Write buffering. Aggregate increments in memory on each app server for 1 second and flush one
INCRBY 37instead of 37INCRs. - Append, then aggregate. Write events to a log (Kafka partitioned by a random key, not the hot ID), and let a stream processor compute the total. The hot key becomes a hot aggregate, updated a few times per second.
- Pre-split exact state. For flash-sale inventory, split stock into buckets (50 buckets of 100 units), each with its own owner, so contention is per bucket and nothing oversells.
Detection
Pre-mark predictable cases such as known celebrities; for the rest, count. Each shard or proxy keeps a count-min sketch or a Space-Saving top-k structure over a sliding window, reports keys above a threshold, and a control plane flips them into "hot mode" (enable salting, extend TTLs, push to L1). Redis offers redis-cli --hotkeys, which works only when maxmemory-policy is an LFU policy, because it reads the eviction counters (redis-cli docs). Since Redis 8.6 the server can also track hot keys itself: HOTKEYS START records the top K keys by share of CPU time or network bytes over a window, with optional sampling and per-slot filters (Redis docs). Managed stores such as DynamoDB shift capacity toward hot partitions and can isolate one heavily used item on its own partition, but that item still tops out at 3,000 read units and 1,000 write units per second (AWS docs).
How it shows up in interviews
Questions that trigger it: design Twitter or Instagram (celebrity posts), design a like or view counter, design a ticketing system, design a distributed cache, design a leaderboard, design a rate limiter keyed by API key. A strong answer separates read-hot from write-hot, sizes the problem with numbers, picks a mechanism for each, and mentions detection. Interviewers then probe: how do you invalidate eight copies consistently, what does a split counter cost on read, what if the hot key is not known in advance, and what if the hot key is a write that needs strong consistency.
Down the stack
Layer 1The contract
A salted read-replica scheme promises that a read returns a value no older than the replication or invalidation delay, typically bounded by a TTL you choose (for example 2 seconds). A split counter promises that the sum is eventually exact once in-flight increments land, with no lost increments as long as each sub-key write is durable; a read during writes may undercount by in-flight operations. A local L1 cache promises nothing about cross-server consistency: two users on different servers can see different values for up to the TTL.
Layer 2The mechanism
In Memcached at Facebook, a miss on a hot key can cause a thundering herd of database queries. Their published design uses leases: on a miss, the server hands out a 64-bit lease token to one client and tells others to wait briefly and retry; the paper describes issuing a token at most once every 10 seconds per key, which cut peak database queries on such keys from 17,000 to 1,300 per second. The token also rejects stale sets if the key was deleted meanwhile (Nishtala et al., NSDI 2013). Redis client-side caching uses CLIENT TRACKING: the server remembers which keys each connection read and sends invalidation messages (over RESP3 push or a redirected Pub/Sub channel) when they change, so an L1 cache can hold values without a fixed TTL. A count-min sketch keeps d rows of w counters; an increment bumps one counter per row and the estimate is the row minimum, which can overestimate but never underestimates (Cormode and Muthukrishnan). Redis's built-in Top-K type pairs such counters with a min-heap of the K heaviest items, using the HeavyKeeper algorithm, whose decay favours large flows over small ones (Redis docs).
Layer 3The machine
A hot Redis key is a hot core: Redis executes commands on one main thread, so one key's operations serialize on one CPU regardless of how many cores the box has. Bandwidth is often the real wall. A 100 KB value read 50,000 times per second is 5 GB/s, about 40 Gbit/s, more than a 25 Gbit NIC can push, so even a perfect CPU path fails. In-process, a single shared counter updated by many threads bounces one 64-byte cache line between cores; each transfer costs on the order of tens to a hundred nanoseconds, which is why LongAdder in Java (Java docs) and per-CPU counters in the Linux kernel stripe a counter across cells, the same split-counter idea at nanosecond scale.
Why it's built this way
Hashing spreads keys evenly because it ignores popularity, so popularity has to be handled separately. A key lives on one owner so that writes have a single serialization point, so read scaling requires copies and write scaling requires splitting the key itself. Copies go stale, so every copy scheme picks a staleness bound (TTL, lease, invalidation). Counting every key exactly costs memory proportional to key count, so detection uses sketches with bounded error.
Diagram
flowchart LR
C["Clients"] --> A["App server with L1 cache"]
A -->|"miss, key not hot"| S1["Shard owning post:42"]
A -->|"key hot: pick #0..#7"| R["Salted copies on 8 shards"]
S1 --> D["Hot key detector (top-k sketch)"]
D -->|"mark hot"| CP["Control plane"]
CP -->|"enable salting and L1"| AWorked example
A social app shows like counts on posts. Normal posts get tens of likes per second. A celebrity post receives 60,000 likes per second and 400,000 count reads per second at peak.
Writes: one Redis shard handles perhaps 80,000 increments per second in practice, and the hot shard also serves other keys. Split the counter into 32 sub-keys spread across shards: each gets about 1,900 increments per second. Add write buffering on 300 app servers with a 500 ms flush: each server sees 200 likes per second, flushes 100 per batch, so the cluster receives about 600 INCRBY calls per second total. A crash loses up to 500 ms of likes on one server: fine for likes, not for payments.
Reads: summing 32 sub-keys per read at 400,000 reads per second would be 12.8 million Redis operations per second. Instead, a background job sums the sub-keys every second and writes likes:42:total. App servers cache that total in L1 for 1 second. With 300 servers, the total key sees at most 300 reads per second. Counts are up to about 2 seconds stale.
In the wild
- Salesforce, 2026: hot keys pin single Redis shards during a cache migration. Moving Marketing Cloud's caching (about 1.5 million cache events per second from 50+ applications) from Memcached to Redis Cluster exposed keys hot enough to saturate one shard's single command thread. Read replicas bought time, the hot keys were moved to local in-memory caches backed by Redis, and the team planned a count-min sketch inside the data access layer to flag keys over a threshold (Salesforce Engineering).
- Healthchecks.io, 2025: one client, one check, 300 requests a second. A misconfigured client pinged a single check about 300 times a second. HAProxy's tarpit rate limiting held each rejected connection open for a second, which used up connection slots, and pulling one load balancer from DNS moved its load onto the others. The service was intermittently down for about 1 hour 40 minutes; the fix was to block the source and answer with an immediate 429 instead (Healthchecks.io).
- Twitter, 2020: measuring skew across 153 cache clusters. An analysis of production Twemcache traces (over 80 TB) found approximately Zipfian popularity, sometimes with very high skew, and that more than 35% of clusters were write-heavy, so hot-key designs cannot assume a read-only head (USENIX OSDI 2020).
Trade-offs
| Option | Strengths | Weaknesses | Choose when |
|---|---|---|---|
| Local L1 cache | Removes load entirely, microsecond reads | Stale per server, memory per instance | Read-hot, staleness of seconds is fine |
| Salted key copies | Read capacity scales with copies | N writes or invalidations per update | Read-hot, values change rarely |
| Split counter | Write capacity scales with splits | Reads cost N lookups, needs a cached sum | Write-hot commutative updates |
| Write buffering | Huge reduction in write rate | Loses buffered data on crash, adds delay | Counters, metrics, analytics |
| Single owner queue | Exact, strongly consistent | Throughput capped by one worker | Inventory, money, anything non-commutative |
Pitfalls in production
- Detecting too late. A viral key goes from cold to melting a shard in seconds. Keep detection windows short (a few seconds) and pre-mark predictable hot keys such as top accounts or scheduled launches.
- Cache stampede on expiry. When a hot key's TTL expires, thousands of servers miss at once. Use leases, request coalescing (single-flight), or early probabilistic refresh before expiry.
- Splitting non-commutative state. Split counters work for increments; they break for "set to X" or check-then-decrement. For stock, split into buckets with their own limits, not a shared total.
- Forgetting writes to salted copies. If an update reaches only some copies, readers flip between values. Write all copies, or store a version and invalidate rather than overwrite.
- Hot keys hiding in secondary indexes. A partition key of
status=PENDINGor today's date makes one index partition hot. Add a random or bucketed suffix to the index key.
Leadership lens
- In design review ask: which IDs in this schema can go viral, and what is the per-key ceiling of the store we chose?
- Decide the staleness budget with product up front. "Counts may lag by 2 seconds" unlocks most of the cheap fixes.
- Make hot-key handling automatic and observable: a dashboard of top keys per shard, alerts on shard skew, and a manual override to force a key into hot mode.
Check yourself
Why does adding more shards not fix a hot key?
Show answer
All requests for one key still route to the single shard that owns it, so extra shards only take load from other keys.
Why do salted copies help reads but not writes?
Show answer
Readers can pick any copy, spreading reads, but every write must reach every copy, so write load multiplies instead of spreading.
A split counter has 16 sub-keys. What does a read cost, and how do you keep reads cheap?
Show answer
Sixteen lookups per read; keep a periodically recomputed cached total so most reads hit one key.
Why is a count-min sketch suitable for hot-key detection?
Show answer
It uses fixed memory regardless of key count and only overestimates, so heavy hitters are never missed.
Sources and further study
Primary
- Cormode and Muthukrishnan: the count-min sketch: the error bounds behind per-shard heavy-hitter detection.
- HeavyKeeper (USENIX ATC 2018): the top-k algorithm Redis uses for elephant flows.
Systems docs
- Redis: HOTKEYS START: server-side hot-key tracking by CPU and network share, from Redis 8.6.
- DynamoDB burst and adaptive capacity: isolating a hot item and the per-partition ceiling that still applies.
- Java
LongAdder: the striped counter that trades memory for throughput under contention.
Books
- Designing Data-Intensive Applications (1st ed.), Martin Kleppmann, Ch 6 "Partitioning": the section on skewed workloads and relieving hot spots describes salting a hot key with a random suffix and the read cost it adds.
Other curricula
- donnemartin/system-design-primer: treats hot spots as a disadvantage of sharding, where a set of power users can make one shard lopsided.
- Azure Architecture Center: Sharding pattern: frames hotspots as a shard-key problem and recommends many small virtual shards so a busy one can be moved cheaply.
Watch
- Scale fearlessly with Amazon DynamoDB adaptive capacity (AWS Events): how a managed store detects and splits hot partitions, and where per-key limits remain.
- Scaling Memcache at Facebook (NSDI talk): leases, thundering herds and hot keys in one of the largest cache deployments.
- Scaling Instagram Infrastructure (InfoQ): practical handling of celebrity traffic and cache thundering herds.
Free resources
- Scaling Memcache at Facebook (USENIX NSDI 2013): leases, regional pools and replication for hot items.
- DynamoDB partition key design (AWS docs): per-partition limits and write sharding with suffixes.
- Redis client-side caching: server-assisted invalidation for local caches.
- Count-min sketch (Wikipedia): the data structure behind cheap heavy-hitter detection.
- Bigtable schema design (Google Cloud): row-key design to avoid hotspotting in range-partitioned stores.