Choosing partition keys
Every container needs a partition key. It decides how data is spread, how much a query costs, and whether you ever hit a throughput or size limit. You cannot change the key of an existing container without migrating the data (a container copy job or the change feed can help).
Logical and physical partitions
All items with the same partition key value form a logical partition. It is limited to 20 GB and its throughput is limited by the physical partition that holds it. Azure manages physical partitions (each up to 50 GB and 10,000 RU/s) and places many logical partitions on them. You control only the key.
Transactions (stored procedures, triggers, transactional batch) work only within one logical partition.
What makes a good key
- High cardinality: many distinct values.
- Even distribution of storage and of request volume.
- It appears in the filter of your most frequent queries, so they hit one partition.
A bad key causes a hot partition: one value gets most traffic, you see 429 errors on that partition while total RU/s looks fine. A date or timestamp alone is a classic bad key for writes, because all new items go to the same partition.
Synthetic partition keys
If no single property works, build one from several, or add a suffix:
| Technique | Example |
|---|---|
| Concatenate properties | deviceId-2026-10-22 |
| Random suffix | sensor7- plus a number 1-10 |
| Pre-calculated suffix (hash) | Lets you read a specific suffix back |
A random suffix spreads writes but makes reads fan out, while a calculated suffix keeps reads targeted.
Hierarchical partition keys
Hierarchical (subpartitioning) keys use up to three levels, for example /tenantId, /userId, /sessionId. Benefits:
- Data for one tenant can grow beyond 20 GB, because the full path forms the logical partition.
- Queries that include the first level or a prefix are routed to the relevant partitions only, not all of them.
In .NET you declare the paths when creating the container and supply a value with PartitionKeyBuilder:
PartitionKey pk = new PartitionKeyBuilder()
.Add(tenantId).Add(userId).Add(sessionId).Build();
Query routing
A query with the partition key in the filter is an in-partition query. Without it, it is a cross-partition (fan-out) query that costs more RUs and latency as the container grows. A filter on id alone is also cross-partition unless the id is the key.
Exam traps
- id is not the partition key by default. Uniqueness is per logical partition, so the same id can exist under two key values.
- A unique key policy is also scoped to the logical partition.
- Low cardinality keys such as country or manufacturer lead to skew.
- Hierarchical keys are not the same as a composite index or a synthetic key.
- The key path cannot be changed in place.
Practice questions
Choose an answer, then review the explanation to check your understanding.
1. IoT devices send readings every second. A few devices send far more data than others. Which partition key avoids hot partitions and still supports reads for one device?
Show answer
Answer: C.A synthetic key with high cardinality spreads writes, and including deviceId keeps one device's data queryable within few partitions.
2. A multitenant app has some very large tenants. Queries filter by tenantId and then userId. What should you use?
Show answer
Answer: A.Hierarchical partition keys (up to three levels) let a large tenant spread over multiple physical partitions while queries that supply the prefix stay targeted.
3. What is the maximum size of one logical partition?
Show answer
Answer: B.A logical partition holds at most 20 GB of data. Keys that grow past that will fail writes.
4. Which .NET SDK class do you use to supply a hierarchical partition key value when reading or writing an item?
Show answer
Answer: C.PartitionKeyBuilder adds each level with Add() and builds the value. PartitionKeyDefinition is for container creation.