Skip to content

3. Partitions and the storage layer

You cannot see partitions. There is no API that lists them, no console page that shows them, and AWS deliberately does not let you manage them. But every throttling incident, every hot-key story and every rule in chapter 8 is a consequence of how they work, so the model is worth building carefully.

PutItem(PK = "CUSTOMER#42", …)
│
▼
hash("CUSTOMER#42") ──► a point in the key space
│
▼
the partition owning that point
│
▼
three storage nodes in three AVAILABILITY ZONES

The partition key is hashed, not stored in order. Two consequences run through everything:

✓ keys spread evenly by construction. there is no
"last partition" hotspot of the kind an ordered
store suffers from (compare CockroachDB ch 3).
✗ you cannot scan partition keys in order. "all
customers between A and M" is not a query. the
ordering you get is WITHIN a partition, by SORT
KEY, and nowhere else.

Cassandra made exactly the same choice, for exactly the same reasons, and its ring diagram is a good mental picture for what is happening here behind the API.

PARTITION (≤ 10 GB)
┌────────────────────────────────────────────┐
│ items sorted by PARTITION KEY hash, then │
│ by SORT KEY within each collection │
│ │
│ B-tree over the sort keys │
│ → equality, range and begins_with are all │
│ ordinary tree operations │
└────────────────────────────────────────────┘

So a Query is: hash to the partition (one hop), then a tree descent and a sequential read of a sorted range. Both halves have costs that depend on the result size, never on the table size. That is the whole latency promise from chapter 1, implemented.

ONE PARTITION = THREE REPLICAS, THREE AZs
┌───────────┐ ┌───────────┐ ┌───────────┐
│ AZ-a │ │ AZ-b │ │ AZ-c │
│ LEADER │──►│ replica │ │ replica │
└───────────┘ └───────────┘ └───────────┘
• a write is acknowledged once the leader and at
least one replica have it durably
• leader election uses Paxos; failover is seconds
and invisible to you
• strongly consistent reads go to the LEADER
• eventually consistent reads may go to any
replica — and are half the price (ch 4, 6)

You never see this machinery, and it explains two things you do see: why strongly consistent reads cost twice as much (they must reach the leader), and why eventually consistent reads occasionally return a value a few hundred milliseconds stale.

This is the number to memorise, because it is the source of most surprises:

PER PARTITION, MAXIMUM
3,000 read capacity units per second
1,000 write capacity units per second
10 GB of data

A table can do millions of requests per second — across many partitions. One partition key cannot exceed those numbers, however large your table’s provisioned capacity is.

TABLE provisioned 100,000 WCU
ALL writes to PK = "GLOBAL#COUNTER"
→ still capped near 1,000 WCU
→ ThroughputExceededException, while 99% of your
provisioned capacity sits unused

That is the hot partition, and it is the single most common DynamoDB failure (chapter 14). It is a modelling problem, not a capacity problem, and no amount of provisioning fixes it.

Partitions split automatically, in two circumstances:

SIZE SPLIT the partition passed 10 GB.
it splits in half by key hash.
THROUGHPUT SPLIT the partition's traffic exceeds
what it can serve. DynamoDB splits
it so the load spreads over more
machines.
A CRITICAL SUBTLETY
A split divides the KEY SPACE. If all the traffic
is on ONE partition key, splitting does not help —
that key lands entirely on one side of the split,
every time, forever.
Splits rescue "many keys, uneven traffic".
They cannot rescue "one key, all the traffic".

Modern DynamoDB does not divide provisioned throughput rigidly by partition count. Adaptive capacity shifts unused throughput toward busier partitions, automatically and continuously.

WITHOUT (old behaviour) 100,000 WCU over 50
partitions = 2,000 each,
strictly
WITH ADAPTIVE CAPACITY a busy partition can
borrow from idle ones, up
to the HARD per-partition
ceiling of 1,000 WCU /
3,000 RCU

This removed a whole class of historical complaints, and it is why older blog posts about “capacity dilution” read as obsolete. What it does not do is raise the per-partition ceiling — physics, and the design, still cap a single key.

The rule that falls out of all of the above:

A GOOD PARTITION KEY HAS
① HIGH CARDINALITY many distinct values
✓ userId, deviceId, tenantId#entityId
✗ status, country, "ORDERS", today's date
② EVEN ACCESS no single value taking a
disproportionate share
✗ one celebrity user, one enormous tenant,
one hot product on launch day
③ MATCHES THE QUERY you can only Query by an
exact partition key, so it
must be a value the caller
actually has

When a naturally hot key is unavoidable, the standard remedy is write sharding:

PK = "PRODUCT#123" → 1 partition, capped
PK = "PRODUCT#123#" + (0..9) → up to 10 partitions
reads fan out over 10 keys and merge

Chapter 13 works through the trade — ten times the write headroom, ten times the read requests — and chapter 8 covers designing so you need it less often.

Item collections and partitions are not the same thing

Section titled “Item collections and partitions are not the same thing”

A subtlety worth getting right, because the words are used loosely.

ITEM COLLECTION all items sharing ONE partition
key value.
PARTITION a physical storage unit holding
MANY item collections whose
hashes fall in its range.
PARTITION #7
┌──────────────────────────────────────────────┐
│ CUSTOMER#42 → PROFILE, ORDER#…, ADDRESS#… │ ← one
│ CUSTOMER#88 → PROFILE, ORDER#… │ item
│ PRODUCT#S1 → META, REVIEW#… │ collection
└──────────────────────────────────────────────┘ each
CONSEQUENCES
• one huge item collection cannot be split across
partitions — it is bounded by the partition's
10 GB
• a busy NEIGHBOUR can consume capacity you were
hoping to use, until adaptive capacity and
splits react
• with an LSI, an item collection is HARD-CAPPED
at 10 GB and writes start failing (ch 7)
FINE a customer with 10,000 orders
a device with a year of daily
summaries
WATCH a tenant with 5 million items —
check the total bytes against 10 GB
REDESIGN "ALL_ORDERS" as a partition key
a date-only key on a busy day
one tenant holding 90% of your data

The practical rule: a partition key value should identify something whose data is bounded by the nature of the thing, not by the size of your business. A customer’s orders are bounded by that customer’s behaviour. “All orders” is bounded by nothing.

YOU CANNOT SO INSTEAD
see or manage partitions design keys well
pin data to a partition use the key prefix
scan keys in order model an index (ch 7)
exceed 1,000 WCU on one key shard the key
fix a hot key with capacity fix the model
Terminal window
# what the service will tell you about distribution
aws cloudwatch get-metric-statistics \
--namespace AWS/DynamoDB --metric-name ThrottledRequests \
--dimensions Name=TableName,Value=AppData \
--start-time … --end-time … --period 60 --statistics Sum

CloudWatch Contributor Insights goes further and names the most-accessed keys directly, which is the fastest route from “we are being throttled” to “this key is the problem” (chapter 13).

  • The partition key is hashed to choose a partition, so distribution is even by construction — and ordering across partition keys does not exist.
  • Within a partition, items are sorted by sort key in a B-tree, so Query costs scale with the result size, never the table size.
  • Every partition is replicated to three availability zones, with a leader that serves strongly consistent reads; eventually consistent reads may hit any replica and cost half as much.
  • The hard ceiling is 3,000 RCU and 1,000 WCU and 10 GB per partition. A table scales far beyond that; one partition key does not.
  • Partitions split by size and by throughput, but a split divides the key space — it cannot help when all traffic is on one key.
  • Adaptive capacity lends unused throughput to busy partitions automatically, and does not raise the per-partition ceiling.
  • A good partition key has high cardinality, even access, and matches how callers query. When a hot key is unavoidable, shard it with a suffix and fan out reads.
  • Splits take time; pre-ramp before known spikes, or use on-demand billing for faster adaptation.
  • Use Contributor Insights to identify the hot key rather than guessing.

Next: reads and writes — the API surface, consistency choices, conditional writes, and what each operation actually costs.

Written by Sepuri Sai Krishna

© 2026CC BY 4.0Source