Amazon DynamoDB
Learn DynamoDB access-pattern modeling, partition distribution, secondary indexes, capacity modes, consistency, streams, and recovery.
What is Amazon DynamoDB?
Amazon DynamoDB is a serverless, fully managed, distributed NoSQL database that stores key-value and document data. Applications read and mutate items through primary keys, secondary indexes, and bounded APIs instead of sending arbitrary joins to a relational query planner.
DynamoDB matters when a request path needs predictable single-digit millisecond performance while traffic and data grow. AWS owns servers, partition management, replication inside a Region, maintenance, and service scaling. Your team still owns the data model, key distribution, consistency contract, capacity and quota boundaries, retry behavior, and recovery drills.
The core invariant is: every important access pattern must map to a key-based operation whose traffic remains distributed. A table with ample aggregate capacity can still throttle when one partition-key value or GSI key range receives too much work.
Known access paths
Strong fit
Use DynamoDB for high-scale request paths with known point reads, bounded range queries, conditional writes, and simple item-centered transactions.
Ad hoc relationships
Weak fit
Do not expect joins, arbitrary multi-attribute filtering, or analytical scans to remain efficient as a transactional table grows.
Serverless is not schemaless thinking
Design responsibility
AWS operates the service. The application must encode entities and relationships into keys, test skew, make retries safe, and prove restore procedures.
Build the mental model from tables, items, and keys
A table is a collection of items. An item is a collection of named attributes, similar to a row with scalar, set, list, map, or document values. Each item can have different non-key attributes, but every item must satisfy the table's primary-key schema. The complete item, including attribute names, must remain within the 400 KB item limit.
Placement and lookup
Partition key
DynamoDB hashes this value to place an item. A simple primary key contains only this attribute, so its value must be unique per item.
Ordered item collection
Sort key
With a composite primary key, items may share a partition key and are ordered by sort key. Prefixes and ranges can retrieve related items without a scan.
Unique identity
Primary key
The partition key alone, or the partition-key and sort-key pair, uniquely identifies one item and defines the direct access contract.
For a commerce table, PK = CUSTOMER#c-42 and SK = ORDER#2026-07-24T09:30:00Z#o-2048 can group one customer's orders and sort them by time. A separate order item can use PK = ORDER#o-2048 for a direct lookup. Duplicating selected facts is often intentional when it serves two bounded access patterns without a join.
Read the official core-components guide when the distinction between item identity, item collections, and secondary indexes needs a deeper walkthrough.
Design from access patterns before drawing a table
Relational modeling often starts with normalized entities and derives queries later. DynamoDB modeling reverses that sequence: list the application's reads and writes, then choose keys and indexes that answer them directly.
1 Contract
Name each operation
Write concrete requests such as “get order by ID,” “list a customer's newest orders,” or “transition one order from pending to paid.”
2 Shape
Define bounds
Record result count, item size, read freshness, write atomicity, peak rate, tenant isolation, and retention for every operation.
3 Model
Assign keys and indexes
Map direct identity to the base key, ordered relationships to sort-key ranges, and required alternate lookup dimensions to a secondary index.
4 Prove
Test skew and failure
Load-test realistic key distributions, retries, concurrency races, GSI pressure, and restore procedures before treating the schema as production-ready.
Access-pattern review
- Prefer
GetItemwhen the complete primary key is known. - Prefer
Querywhen one partition-key value and an optional sort-key condition bound the result. - Treat
Scanas deliberate table-wide work, not the fallback for an unmodeled request path. - Remember that a
FilterExpressionis applied after DynamoDB reads candidate items; it does not turn a broad read into a cheap key lookup. - Design both the base table and every GSI for high-cardinality, well-distributed partition-key values.
AWS's current DynamoDB data-modeling guide describes single-table and multiple-table foundations. Single-table design is a technique, not a requirement: use it when colocated item collections simplify real operations, not as a badge of sophistication.
Separate aggregate capacity from hot-key pressure
DynamoDB rounds write capacity by item size: one write capacity unit supports one write per second for an item up to 1 KB. A strongly consistent read of an item up to 4 KB consumes one read capacity unit; an eventually consistent read of the same size uses half as much read capacity.
Each partition is designed for up to 1,000 write capacity units and 3,000 read capacity units per second. Adaptive capacity helps uneven workloads, but it does not remove the per-partition ceiling. The same distribution problem applies independently to the base table and every GSI.
Can the hottest key range carry the writes?
Loading workload profiles, capacity-unit rules, and partition bounds.
Loading the DynamoDB workload model.
Choose capacity mode from traffic evidence
- On-demand: pays per request and removes a configured table-level capacity target. It is a strong default for new, spiky, or unpredictable workloads, but hot key ranges, configured maximum throughput, account quotas, and rapid growth beyond warm throughput still matter.
- Provisioned: configures RCU and WCU directly, optionally with auto scaling. It fits steady, measurable workloads when the team can manage headroom and scale before scheduled bursts.
- Both modes: require realistic partition-key distribution, per-index monitoring, bounded clients, and diagnosis from the throttling reason rather than a generic “add capacity” response.
Use the official partition-key guidance and capacity-mode guide to verify service behavior as limits and features evolve.
Add secondary indexes only for named queries
A secondary index stores a separate, projected view of table items under another key schema. It makes an alternate query efficient, but it adds write work, storage, and a new distribution surface.
Authoritative key
Base table
Supports eventually consistent reads by default and optional strongly consistent reads for GetItem, Query, and Scan.
Same partition key
Local secondary index
Uses the base partition key with another sort key. It shares table capacity and can support strong reads, but must be created with the table and has item-collection constraints.
Alternate partition key
Global secondary index
Can use different partition and sort keys. Updates are asynchronous, reads are always eventually consistent, and its partitions and capacity are independent of the base table.
In provisioned mode, a GSI has separate RCU and WCU. If an index cannot absorb base table updates, DynamoDB can throttle the base-table write to maintain index consistency. A low-cardinality GSI partition key such as status = OPEN can therefore become hot even when the base table's customer IDs distribute perfectly.
Review AWS's read-consistency contract and GSI back-pressure guide before promising read-after-write behavior or diagnosing write throttling.
Choose consistency, correctness, and recovery together
Read consistency answers what a read may observe. Conditional writes and transactions answer whether concurrent mutations preserve business invariants. Backups answer how operators recover from a valid API call that produced the wrong business state. None of these controls substitutes for the other two.
Choose the contract, not just the API
Loading workload contracts and DynamoDB decision boundaries.
Loading consistency, index, and recovery scenarios.
Correctness boundaries
- A single
PutItem,UpdateItem, orDeleteItemis atomic for its item. - A condition expression rejects a write when the current item no longer satisfies a version, existence, state, or numeric invariant.
TransactWriteItemsgroups multiple item actions into one ACID operation that commits or cancels as a unit.- A strongly consistent pre-read does not lock the item. Use a condition or transaction at mutation time to prevent lost updates and invalid transitions.
Make throttles, retries, and stream delivery harmless
Transient throttling is part of the API contract. A reliable client slows down, retries only safe work, and preserves the identity of the business operation.
Bounded DynamoDB mutation and event path
The write invariant is enforced before the table changes. Stream-triggered consumers still need destination-side idempotency because failed batches can be retried.
Identify
Application command
Carry a stable order, request, or event identifier so a retry refers to the same business operation.
Protect
Condition or transaction
Check the current invariant atomically and commit one item or an all-or-nothing item set.
Persist
DynamoDB table
Return success only after the write is durably persisted, or return a specific condition, transaction, quota, or throttling failure.
React
Stream consumer
Process change records with an idempotency boundary so a retried Lambda batch does not repeat a charge, email, or projection mutation.
Retry rules
- Use AWS SDK retry behavior or bounded exponential backoff with jitter for throttling and retryable service failures.
- Inspect
ThrottlingReasonand the affected resource ARN; table capacity, key-range pressure, and GSI pressure require different fixes. BatchWriteItemis not atomic as a whole. Retry only returnedUnprocessedItems, not every request that already succeeded.- Use conditional writes, transaction client tokens where supported, and stable operation IDs to make repeated requests safe.
- Send exhausted work to a visible failure path with enough context to replay it deliberately; do not retry forever inside a request thread.
DynamoDB Streams captures item changes for event-driven processing. A Lambda trigger retries a failed batch, so the consumer must be idempotent even when the source record itself has a stable event ID.
Treat backups as a recovery workflow, not a checkbox
Point-in-time recovery continuously captures table recovery points at per-second granularity for a configurable period from 1 to 35 days. A restore creates a new table; it does not rewind the current table in place. The team must validate the restored data and deliberately reapply or verify settings such as IAM policies, auto scaling, alarms, streams, TTL, tags, deletion protection, and PITR itself.
On-demand backups are useful for named milestones and longer-lived snapshots. Replicas, global tables, GSIs, and Streams improve availability or propagate changes, but they can also propagate an accidental delete or corrupt write. They are not independent rollback history.
1 Stop
Detect and contain
Block the bad writer, capture the failure window, and preserve logs before another deployment or repair changes the evidence.
2 Recover
Restore to a new table
Choose a point before the mutation and restore without consuming source-table provisioned throughput.
3 Prove
Validate data and settings
Check representative keys, counts, indexes, encryption, access policy, streams, TTL, alarms, and application compatibility.
4 Resume
Cut over and reconcile
Redirect traffic deliberately, reconcile valid writes after the restore point, monitor the new table, and keep a rollback path until the incident is closed.
See the official PITR guide and restore behavior when writing the runbook.
Put the contracts into focused code
The first example turns named access patterns into concrete primary-key, range-query, and transaction requests. The key strings make entity type and relationship intent visible during debugging.
BatchWriteItem may return a partially processed result. This loop resubmits only the unprocessed work with capped exponential backoff and jitter.
A stream-triggered Lambda can see a batch again after failure. The final example transactionally claims the stream event ID and creates one outbox intent. A downstream worker can then retry that stable intent without silently losing or duplicating the derived work.
Keep the key grammar, condition expressions, retry policy, and restore procedure under version control. A DynamoDB table can be operationally healthy while the application's access contract is still incorrect.