2026-07-16 · 8 min read
DynamoDB Data Modeling for DevOps Engineers
A practical introduction to DynamoDB data modeling, partition keys, single-table design, access patterns, and the mistakes that cause hot partitions and runaway cost.

DynamoDB Data Modeling for DevOps Engineers
DynamoDB is fast, serverless, and scales endlessly, if you model your data the way it wants. Coming from relational databases, the instinct is to normalize and query flexibly. DynamoDB punishes that. Here's the mental shift and the practical rules.
The core rule: model around access patterns, not entities
In SQL you design tables around your data and write whatever queries you need later. In DynamoDB you do the opposite: list your access patterns first, then design keys so each pattern is a single, efficient lookup. If you don't know your queries, you can't model DynamoDB well.
Partition key and sort key
Every item has a partition key (PK) that determines which physical partition it lives on, and optionally a sort key (SK) for ordering and range queries within a partition.
- Good PK: high-cardinality, evenly accessed (e.g.
USER#123). Spreads load. - Bad PK: low-cardinality or skewed (e.g.
statuswith valuesactive/inactive). Creates a hot partition: one partition gets hammered, throttling everything on it.
Single-table design
The DynamoDB-native pattern is to put many entity types in one table, distinguished by key patterns:
PK SK Attributes
USER#123 PROFILE name, email
USER#123 ORDER#2026-01-01 total, status
ORDER#555 ITEM#1 sku, qty
This lets one query fetch a user and their orders in a single round trip (PK = USER#123). It
feels alien at first, but it's how you avoid N+1 lookups.
Secondary indexes for extra access patterns
When you need to query by something other than the PK:
- GSI (Global Secondary Index): a different PK/SK; eventually consistent; its own capacity.
- LSI (Local Secondary Index): same PK, different SK; strongly consistent; set at table creation.
A common pattern: an "inverted index" GSI that flips PK and SK so you can query the relationship the other direction.
Billing: on-demand vs provisioned
- On-demand: pay per request, scales instantly, zero capacity planning. Best for spiky or unknown traffic, and the right default for most apps.
- Provisioned (+ auto-scaling): cheaper for steady, predictable, high volume.
(My aws-serverless-api-terraform uses on-demand, nothing to tune.)
The mistakes that bite
- Hot partitions from a low-cardinality PK → throttling. Pick a PK that spreads evenly.
- Scans in production.
Scanreads the whole table; it's fine for admin/batch, a disaster on a hot path. Design so youQuery, notScan. - Unbounded item growth. Items max out at 400 KB; don't append forever to one item.
- Treating it like SQL. No joins, no flexible ad-hoc queries. If you need those, you modeled wrong (or you need a different database).
A practical workflow
- Write down every access pattern ("get user by id", "list a user's orders newest-first", …).
- Design PK/SK so each pattern is one
QueryorGetItem. - Add GSIs only for patterns the main key can't serve.
- Start on-demand billing; switch to provisioned only with steady, proven volume.
- Load-test for hot partitions before you trust it at scale.
DynamoDB rewards up-front modeling and punishes improvisation. Do the access-pattern work first and it's one of the best databases in the cloud; skip it and you'll fight it forever.
Building serverless on AWS and unsure how to model your data? That's part of my consulting, reach out.