Amazon DynamoDB is a fully managed NoSQL database that answers in single-digit milliseconds whether you store a thousand items or a few billion. That performance is not automatic, though: it comes from designing keys around your queries. Teams that model DynamoDB like a relational database end up with slow scans and surprising bills. This guide covers the concepts that matter.
Tables, items and attributes
A table holds items (rows), each a set of attributes. Apart from the key, items in the same table can have completely different attributes. There are no joins and no fixed schema; each item can be up to 400 KB.
The primary key
Every table has a primary key, defined when you create it and not changeable later:
- Partition key only (simple key): each value identifies exactly one item. Example:
userId. - Partition key + sort key (composite key): many items share a partition key, ordered by the sort key. Example:
customerId+orderDate.
DynamoDB hashes the partition key to decide which physical partition stores the item. Items with the same partition key live together, sorted by the sort key, which is what makes range queries fast.
Query vs Scan
| Query | Scan | |
|---|---|---|
| Needs | An exact partition key (plus optional sort key condition) | Nothing |
| Reads | Only the matching items in one partition | Every item in the table |
| Cost and speed | Proportional to what you return | Proportional to the table size |
"Get the last 20 orders of customer C42" is a Query on customerId = C42 with ScanIndexForward=false and Limit=20. "Find all orders over $500" is a Scan, unless you design an index for it. A FilterExpression on a Scan still reads (and charges for) every item; it only filters what comes back.
Design keys from your access patterns
Write down every question the application asks before creating the table:
- Get a customer by ID.
- List a customer's orders, newest first.
- Get an order by order ID.
- List orders by status for the warehouse.
Then design keys so each question is a GetItem or Query. Patterns 1 and 2 can share one table with PK = CUSTOMER#C42 and sort keys like PROFILE and ORDER#2026-10-20#O981. Pattern 4 needs a secondary index. Storing several entity types in one table this way is called single-table design; it is powerful but optional. Several well-designed tables are fine too.
Secondary indexes
- Global secondary index (GSI): a different partition and sort key over the same data, for example
status+orderDate. Can be added anytime, has its own capacity, and is updated asynchronously (eventually consistent reads only). - Local secondary index (LSI): same partition key, different sort key. Must be created with the table, supports strongly consistent reads, and limits each partition key's data to 10 GB.
Project only the attributes the index's queries need to keep storage and write costs down. Every write to the table also writes to each index that includes the item.
Capacity modes
- On-demand: pay per request, no capacity planning, scales instantly for most traffic. Great default for new or spiky workloads.
- Provisioned: you set read and write capacity units (with optional auto scaling). Cheaper for steady, predictable traffic, and reserved capacity lowers it further.
One read capacity unit covers one strongly consistent read per second of up to 4 KB (or two eventually consistent reads); one write capacity unit covers one write per second of up to 1 KB. Large items cost proportionally more.
Hot partitions
Throughput is spread across partitions, and each partition has limits. If most traffic hits one partition key (a celebrity's profile, "today" as a key, a single tenant), that partition throttles while others sit idle. DynamoDB's adaptive capacity helps, but good keys help more: choose high-cardinality partition keys, and for extreme write hot spots, add a suffix (write sharding) and fan in reads.
Features worth knowing
- TTL deletes expired items automatically at no extra charge: perfect for sessions and temporary data.
- DynamoDB Streams emits every change, which Lambda can consume to update search indexes, send notifications or aggregate data.
- Transactions give all-or-nothing writes across up to 100 items.
- Conditional writes (
attribute_not_exists) prevent duplicates and lost updates. - Point-in-time recovery and on-demand backups protect against mistakes.
- DAX is an in-memory cache for microsecond reads on read-heavy tables.
Practice it
Start with the free Getting Started with Amazon DynamoDB, then model access patterns in DynamoDB Data Access: Partition Keys, GSIs, Query vs Scan. Build on it with DynamoDB Streams to Lambda and debug Sensor Ingest Throttled to Zero.
FAQ
Is DynamoDB good for analytics?
Not directly. It is built for known, key-based access. Export to S3 and query with Athena, or stream changes into an analytics store.
Can I change a table's primary key?
No. You create a new table and migrate the data, which is why modeling access patterns first matters.
Strongly or eventually consistent reads?
Eventually consistent reads are the default, cost half, and are usually only milliseconds behind. Use strongly consistent reads when you must see a write immediately (not available on GSIs).