DynamoDB Hot Key Mitigation: Write Sharding With Scatter-Gather Reads

DynamoDB Hot Key Mitigation: Write Sharding With Scatter-Gather Reads
9 views
One celebrity partition key will throttle an otherwise healthy table long before you hit account limits. Write sharding spreads load across N physical keys; scatter-gather reads stitch them back. The senior move is choosing N, bounding gather fan-out, and accepting that absolute counters become approximate unless you add a rollup path.
⚡ TL;DR: Append a calculated shard suffix to hot keys (pk#shard = hash(id) % N). Write to one shard; read with bounded parallel GetItem/Query. Cap N by p99 gather latency. Pair with Multi-Tenant Rate Limits and Lambda Reserved Concurrency Bulkheads.

DynamoDB Hot Key Mitigation: production guidance

python
# ops/hot_key.py
# CloudWatch Contributor Insights / CloudWatch metrics — illustrative
THRESH_WCU = 800 # per partition soft alarm under on-demand

def is_hot(partition_key: str, consumed_wcu: float) -> bool:
 return consumed_wcu >= THRESH_WCU # ✅ alert + auto-recommend shard count
❌ Waiting for ProvisionedThroughputExceeded in user-facing APIs — by then you are already shedding traffic.

Calculated write shards

typescript
// ddb/shard.ts
import { createHash } from "node:crypto";

export function shardOf(id: string, n: number): number {
 const h = createHash("sha256").update(id).digest();
 return h.readUInt32BE(0) % n; // ✅ stable for a given N
}

export function shardedPk(base: string, id: string, n: number): string {
 return `${base}#${shardOf(id, n)}`;
}

export async function incrCounter(tenantId: string, delta: number, n = 16) {
 // For pure counters, shard by random/round-robin instead of id hash:
 const shard = Math.floor(Math.random() * n);
 await ddb.update({
 Key: { pk: `tenant#${tenantId}#cnt#${shard}`, sk: "total" },
 UpdateExpression: "ADD #c :d",
 ExpressionAttributeNames: { "#c": "count" },
 ExpressionAttributeValues: { ":d": delta },
 });
}
Use random/RR shards for write-heavy counters; use hash(id) % N when each entity must land on one shard for point reads.

Scatter-gather with a latency budget

typescript
// ddb/gather.ts
export async function gatherCount(tenantId: string, n = 16, budgetMs = 40) {
 const started = Date.now();
 const keys = Array.from({ length: n }, (_, i) => ({
 pk: `tenant#${tenantId}#cnt#${i}`,
 sk: "total",
 }));
 // ✅ Parallel GetItem with AbortSignal budget
 const parts = await Promise.all(
 keys.map((Key) =>
 ddb.get({ Key, AbortSignal: AbortSignal.timeout(budgetMs) }).catch(() => null)
 )
 );
 const sum = parts.reduce((a, p) => a + (p?.Item?.count ?? 0), 0);
 metrics.timing("GatherMs", Date.now() - started);
 return { sum, partial: parts.some((p) => p === null) };
}
N Write relief Gather p99 (illustrative) Guidance
4 Mild +5–10 ms First step
16 Strong +15–30 ms Common sweet spot
64 Heavy +40–80 ms Needs rollup

Rollups beat giant gathers

For dashboards, maintain an async rollup via Streams → Lambda that writes tenant#id#cnt#rollup. Clients read the rollup; writers keep sharding. Same outbox mindset as DynamoDB Streams Outbox.

Closing checklist

✅ Dos – ✅ Alarm on per-partition WCU/RCU before hard throttles – ✅ Pick shard function deliberately (hash vs random) – ✅ Bound gather parallelism and time budget – ✅ Expose partial results instead of lying – ✅ Add rollups when N grows past ~16 ❌ Don’ts – ❌ Don’t shard every key “just in case” – ❌ Don’t gather 256 keys on the user path – ❌ Don’t change N without a dual-read migration plan – ❌ Don’t ignore skew inside shards (tenant-of-tenants) – ❌ Don’t treat approximate counters as financial truth

Related reading

Last updated on September 11, 2026 Related: DynamoDB Single-Table Design: The Pattern That Scales to Billions of Items; How to Build a Hyper-Scale Serverless API Platform on AWS with API Gateway, Lambda, DynamoDB, and CloudFront; How to Build a Real-Time Fraud Detection and Decisioning Architecture on AWS with EventBridge, Step Functions, and DynamoDB.

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.