system-design · beginner

Hot Partition / Hot Key — When One Shard Takes All the Heat

The Central Question

Consider a sharded social media platform running on the DataLab platform (datalab.com) processing 100,000,000 queries per day:


Because all records for user_id = 9901 map to Shard 4, Shard 4 receives 50,000 queries per second, driving CPU utilization to 100% and connection pool exhaustion.

Meanwhile, the other 15 database shard nodes sit virtually idle at 3% CPU.

This condition is a Hot Key triggering a Hot Partition.

Adding more database shards to the cluster will NOT fix this outage—because all requests for user_id = 9901 will still route exclusively to Shard 4 based on its shard key hash!

This lesson answers one central question: Why do Hot Keys bypass standard database sharding and consistent hashing, and how do engineers eliminate traffic bottlenecks using Key Salting, Local Micro-Caching, and Scatter-Gather Read Aggregation?


Defining the Distinction: Hot Key vs. Hot Partition

While often used interchangeably, engineers distinguish between the root cause (Hot Key) and the system symptom (Hot Partition):

flowchart TD
  Cause[Hot Key: Root Cause] -->|Routes to single node| Symptom[Hot Partition: System Symptom]
  
  Cause --> KeyDesc["A single data item (e.g. Celebrity Post or Viral Product)<br/>receives an extreme spike in read or write QPS."]
  Symptom --> PartDesc["A specific physical database node hits 100% CPU/Disk,<br/>degrading all unrelated keys stored on that node."]

Figure 1: Relationship between root cause Hot Keys and resulting Hot Partitions.

The Partition Skew Ratio ($S_{\text{skew}}$)

The degree of traffic imbalance across a $K$-node sharded cluster is mathematically quantified by the **Partition Skew Ratio**:

$$S_{\text{skew}} = \frac{Q_{\text{max}}}{Q_{\text{avg}}}$$

Where $Q_{\text{max}}$ is the QPS hitting the busiest shard node, and $Q_{\text{avg}}$ is the average QPS across all nodes.



Architectural Mitigation 1: Key Salting (Random Suffixing)

To break a single Hot Key across multiple physical database shards, systems implement Key Salting:

flowchart TD
  subgraph Un-Salted Hot Key Bottleneck
    Client1[50,000 Concurrent Writes] --> Router1{Hash 'post:9901'}
    Router1 -->|100% Traffic| Node4[(Shard Node 4 - 100% CPU CRASH)]
  end

subgraph Salted Key Distribution
Client2[50,000 Concurrent Writes] --> SaltRouter{Append Random Salt 1..4}
SaltRouter -->|25% Traffic: post:9901#1| S1[(Shard Node 1)]
SaltRouter -->|25% Traffic: post:9901#2| S2[(Shard Node 2)]
SaltRouter -->|25% Traffic: post:9901#3| S3[(Shard Node 3)]
SaltRouter -->|25% Traffic: post:9901#4| S4[(Shard Node 4)]
end

Figure 2: Key Salting distributing a viral key across 4 independent database shards.

How Key Salting Works

  1. Write Path: When writing a hot counter or post (e.g. post:9901), the application appends a random integer salt between $1$ and $N$ (e.g. post:9901#rand(1,4)).
  2. Distribution: Each salted key hashes to a different physical shard server, dividing the 50,000 QPS load into four manageable 12,500 QPS streams across Shard 1, Shard 2, Shard 3, and Shard 4.
  3. Read Aggregation: To read the total count, the application queries all $N$ salted keys in parallel (post:9901#1 through post:9901#4) and sums the results.

Architectural Mitigation 2: Local Micro-Caching (L1 Pod RAM)

For read-heavy Hot Keys, querying even a distributed cache cluster (Redis) can cause single Redis node CPU saturation. Systems deploy Local Micro-Caching inside application pod memory:

sequenceDiagram
    autonumber
    actor User as Client Browser
    participant Pod as Application Server Pod
    participant L1Mem as Local RAM (5s Micro-Cache)
    participant DB as Database Shard 4
    
    User->>Pod: 1. GET /posts/9901
    Pod->>L1Mem: 2. Check local sync.Map
    alt L1 Micro-Cache Hit (5-second TTL)
        L1Mem-->>Pod: 3. Return Cached Record (50 microseconds)
        Pod-->>User: 4. HTTP 200 (Instant!)
    else L1 Micro-Cache Miss
        Pod->>DB: 5. Query Database Shard 4 (15ms)
        DB-->>Pod: 6. Return Post Payload
        Pod->>L1Mem: 7. Store in L1 RAM with TTL = 5s
        Pod-->>User: 8. HTTP 200
    end

Figure 3: Local Micro-Caching in application pod RAM absorbing read traffic spikes.

Why Micro-Caching is Extremely Effective

A 5-second TTL micro-cache in application pod memory means each of 100 application server pods queries Database `Shard 4` **at most once every 5 seconds** ($\frac{100}{5} = 20\text{ QPS}$ total hitting DB), collapsing 50,000 QPS down to 20 QPS!

Asynchronous Write-Behind Aggregation for Salted Counters

When using Key Salting (`counter_key#1` through `counter_key#10`), executing scatter-gather reads across 10 database shards for every user page load introduces network latency overhead. To eliminate read fan-out, systems combine Key Salting with **Asynchronous Write-Behind Aggregation**:
  1. Incoming high-velocity write updates hit random salted keys (counter_key#rand(1,10)) in high-speed Redis RAM.
  2. A background worker process periodically runs an aggregation loop every 1 second, executing MGET counter_key#1 ... counter_key#10, summing the totals, and writing the consolidated single total to a primary read-only key counter_key:total.
  3. Application clients read counter_key:total in a single $O(1)$ lookup, eliminating scatter-gather fan-out completely.

Single-Tenant Shard Isolation (VIP Tenant Tiering)

In multi-tenant SaaS architectures (such as Shopify or Salesforce), 99% of customers are small merchants producing 5 QPS, while 1 enterprise customer (e.g. Nike) produces 20,000 QPS. Storing Nike on a shared multi-tenant database shard causes **Noisy Neighbor Outages** for small merchants. Systems deploy **Single-Tenant Shard Isolation**:

Read Replica Fan-Out Offloading

For read-heavy hot keys that cannot be cached locally (e.g. real-time stock ticker prices that change every 10 milliseconds), systems deploy a pool of **Dedicated Read Replicas** behind a single primary shard. All write mutations hit the Primary node, while incoming read QPS is load-balanced across 16 read replicas, dividing a 50,000 QPS read stream into 3,125 QPS per replica.

Detecting Hot Keys via Redis --hotkeys & eBPF Tracing

To identify hot keys before they trigger a Sev-1 database outage, SRE teams use automated monitoring tools:

Complete Worked Example: Go Hot Key Salted Router & Scatter Aggregator

Let's inspect a complete Go implementation of a Hot Key Salted Router and Scatter Aggregator for the DataLab platform (datalab.com).

package main

import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)

type SaltedKeyRouter struct {
mu sync.RWMutex
saltBuckets int
shardData map[string]int64 // Simulated Shard Storage: Key -> Counter
}

func NewSaltedKeyRouter(saltBuckets int) *SaltedKeyRouter {
return &SaltedKeyRouter{
saltBuckets: saltBuckets,
shardData: make(map[string]int64),
}
}

func (r *SaltedKeyRouter) IncrementSaltedCounter(baseKey string, amount int64) {
// Append random salt (1..saltBuckets)
salt := rand.Intn(r.saltBuckets) + 1
saltedKey := fmt.Sprintf("%s#%d", baseKey, salt)

r.mu.Lock()
r.shardData[saltedKey] += amount
r.mu.Unlock()

fmt.Printf("[SALTED WRITE] Distributed increment on %s (+%d)\n", saltedKey, amount)
}

func (r *SaltedKeyRouter) GetAggregatedCounter(baseKey string) int64 {
var total int64
var wg sync.WaitGroup
var mu sync.Mutex

// Parallel Scatter-Gather across all salt buckets
for i := 1; i <= r.saltBuckets; i++ {
wg.Add(1)
go func(bucket int) {
defer wg.Done()
saltedKey := fmt.Sprintf("%s#%d", baseKey, bucket)

r.mu.RLock()
val := r.shardData[saltedKey]
r.mu.RUnlock()

mu.Lock()
total += val
mu.Unlock()
}(i)
}

wg.Wait()
fmt.Printf("[SCATTER AGGREGATE] Aggregated total for %s across %d buckets: %d\n", baseKey, r.saltBuckets, total)
return total
}


Failure Modes and Engineering Mitigations

Failure ScenarioRoot CauseSystem SymptomDetection SignalMitigation Strategy
1. Un-Mitigated Hot Tenant CollapseEnterprise Customer 901 shares a database shard with 1,000 small tenants.Customer 901 traffic spike crashes Shard 1, taking down 1,000 small tenants (Blast Radius Spreading).High latency and HTTP 500 errors for all tenants hosted on Shard 1.Isolate Hot Tenants onto Dedicated Single-Tenant Shards.
2. Key Salting Read Fan-Out LatencyApplication salts key across 100 buckets, forcing every read query to aggregate 100 database shards.Read query latency increases from $2\text{ms}$ to $250\text{ms}$ due to scatter-gather overhead.High CPU and fan-out metrics on application API gateways.Keep salt bucket count small ($N=4$ to $10$) or aggregate totals asynchronously.
3. Stale Micro-Cache ReadsLocal application pod RAM micro-cache uses 60-second TTL during fast-moving stock trading.Traders see outdated stock prices and execute orders on stale data.Financial reconciliation audit errors post-transaction.Limit local micro-cache TTLs to Short Windows (1 to 3 seconds) for real-time data.
4. Hot Key Redis Node SaturationViral key cached in Redis Cluster still routes to a single Redis node thread.Redis node CPU hits 100%; single thread drops incoming TCP socket connections.Single Redis instance CPU 100% alerts while cluster average CPU is 5%.Replicate hot keys in Redis with Random Key Prefixes or use L1 Local RAM Caching.

What You Should Remember

  1. Hot Keys bypass standard sharding: Adding more database shards does not fix a Hot Key because all requests route to the same node based on shard key hash.
  2. Quantify traffic skew with Partition Skew Ratio: Monitor $S_{\text{skew}} = \frac{Q_{\text{max}}}{Q_{\text{avg}}}$ to detect hot partitions before node crashes occur.
  3. Use Key Salting for hot writes: Append random suffixes (key#rand(1,N)) to scatter hot writes across $N$ independent physical shards.
  4. Deploy Local Micro-Caching for hot reads: Cache hot records in application pod RAM for 1-5 seconds to collapse 50,000 QPS down to 20 QPS on databases.
  5. Isolate Enterprise Hot Tenants: Move massive single-tenant customers off shared multi-tenant shards onto dedicated single-tenant database nodes.

Glossary of Terms

TermDefinition
Hot KeyA single database record or cache key receiving an extreme, disproportionate volume of read/write queries.
Hot PartitionA physical database shard server experiencing 100% CPU/disk saturation due to traffic skew.
Partition Skew Ratio ($S_{\text{skew}}$)The ratio of maximum single-node QPS to average cluster node QPS ($S_{\text{skew}} = \frac{Q_{\text{max}}}{Q_{\text{avg}}}$).
Key SaltingAppending random integer suffixes to a key to distribute writes across multiple physical shards.
Local Micro-CachingCaching hot records in application pod local RAM for ultra-short windows (1-5s) to absorb read bursts.
Blast RadiusThe scope of system components affected when an isolated node or tenant experiences a failure.

Practice Scenario and Self-Assessment

Architecture Scenario

You are designing the ticketing platform for a global concert venue (`tickets.datalab.com`): **Questions**:
  1. Formulate the Key Salting and Scatter Aggregation architecture to process 50,000 seat reservation updates per second.
  2. Detail how your API prevents overselling when combining Key Salting with asynchronous bucket aggregation.

Interactive Self-Assessment

All 50,000 QPS for that key still map to the exact same shard node based on its shard key hash.

Adding shards automatically formats the underlying NVMe SSD disk drives.

Adding shards revokes edge HTTPS TLS encryption certificates on load balancers.

Adding shards cuts physical CPU hardware clock speeds in half across all nodes.

Each application pod serves reads from local RAM, querying the database at most once every 3 seconds per pod.

Local micro-caching automatically converts relational SQL schemas into un-indexed CSV files.

Local micro-caching replaces public DNS nameservers with local hosts entries.

Local micro-caching reboots operating system hypervisors across all application pods.


What to Learn Next

Track: Data, Storage and Messaging

Previous: Database Sharding — Split Data Across Many Machines

Next: How to Scale a Database — The Progressive Scaling Ladder

By Shubham Jain

All articles · Study paths

Shubham Jain · Learning Lab