Amazon DynamoDB β Part 1: Foundations & Core Concepts
maang.io System Design Series
What is this?
Imagine a colossal warehouse that stores your stuff β but you are never allowed inside. You walk up to a front desk, hand over a labelled box, and a concierge says "got it" almost instantly. Later you come back, give the concierge the label, and your box is produced in a blink. You never see the shelves, you never hire the staff, you never worry that a forklift broke or a building lost power. Behind the wall, robots file every box into the right aisle, keep three copies in three separate buildings, and add more robots the moment you start shipping more boxes. You pay for exactly the desk service you use, and the concierge promises: no matter how much you store, retrieving one box always takes about the same tiny moment.
That warehouse is Amazon DynamoDB β a fully managed, serverless key-value and document database. You never provision a server, patch an OS, run a repair, or think about a cluster. You define your keys, hand DynamoDB your items, and it delivers single-digit-millisecond reads and writes at any scale, from a hobby app to Amazon.com's own shopping cart on Prime Day.
The one-line idea: DynamoDB is a Dynamo-family distributed store β like Apache Cassandra, its self-managed cousin β but Amazon operates every part of it for you. You trade the flexibility of running your own cluster for a hard promise: predictable low latency at any scale, with zero operations, as long as you model your data around how you'll query it.
We'll keep coming back to that warehouse-with-a-concierge picture across all four parts β the front desk, the aisles, the three buildings, the aisle foreman who authorizes every change. By the end, you'll know exactly what each one is really doing.
1. Why DynamoDB exists β the pain before it
The story starts with pain that Amazon felt itself. In the 2004 holiday season, parts of Amazon.com's relational databases buckled under load. The shopping cart β which must never reject a write, even if a datacenter is on fire β was a bad fit for a single-leader SQL box that stops taking writes when the leader falls over. Out of that came the famous 2007 Dynamo paper: a highly available key-value store using consistent hashing, replication, and eventual consistency. (That paper is the direct ancestor of Cassandra β read the Consistent Hashing and Apache Cassandra topics for that lineage.)
But the original Dynamo had a catch: every team ran their own instance. It was powerful and notoriously fiddly to operate β you tuned it, scaled it, repaired it, and lived with its eventual-consistency quirks. Amazon learned that most teams didn't want a database toolkit; they wanted a dial tone. So in 2012 they built DynamoDB: same distributed DNA, but delivered as a managed service where the hard parts β partitioning, replication, leader election, failover, scaling, patching β are Amazon's job, not yours.
What was painful before, and what DynamoDB removes:
- Capacity planning and re-sharding. With a self-managed store you provision nodes, watch them fill, and re-shard by hand. DynamoDB splits and moves partitions for you, invisibly.
- Operational toil. No repairs to schedule, no compaction to babysit, no node to replace at 3 a.m.
- Unpredictable latency at scale. DynamoDB's whole design goal is predictable performance: a read of one item costs the same whether the table holds 1 KB or 100 TB.
- Availability math. Every item is automatically replicated across three Availability Zones (physically separate datacenters) in a region. Losing a machine β or a whole building β is a non-event.
π‘ The mental shift: with Cassandra you think in nodes and clusters. With DynamoDB you think in keys and capacity. The machines still exist behind the wall β you just never name them.
2. Core concepts & vocabulary β the mental model
DynamoDB has a small vocabulary. Learn these seven words and the rest of the course clicks into place.
- Table β a collection of items. Unlike SQL, a table has no fixed columns. The only thing every item must share is its key.
- Item β one record (like a row). Max 400 KB. An item is a bag of attributes.
- Attribute β a nameβvalue pair (like a column, but per-item). Types include string, number, binary, boolean, list, map, set. Two items in the same table can have completely different attributes.
- Primary key β how every item is uniquely identified and physically placed. Two shapes:
- Partition key (a.k.a. hash key) β required. DynamoDB hashes it to decide which partition (which warehouse aisle) the item lives in. A table with only a partition key is a pure key-value store.
- Sort key (a.k.a. range key) β optional. When present, the key is composite: many items share a partition key and are stored sorted by the sort key within that partition. This is what unlocks range queries like "give me this user's last 20 orders."
- Partition β the physical unit of storage. A table is split into many partitions; each holds a slice of the key space, lives on SSDs, and is replicated across three AZs. This is the star of Part 2.
- Capacity β how you pay for throughput, in RCUs (read capacity units) and WCUs (write capacity units), or in on-demand per-request pricing. The full math is Part 3.
- Consistency β a per-read choice: eventually consistent (cheaper, maybe slightly stale) or strongly consistent (always the latest, served by the partition's leader). Part 3.
Two more you'll meet constantly:
- GSI / LSI β Global and Local Secondary Indexes: alternate ways to query a table by attributes that aren't your primary key. Part 3 dissects the difference (it's a favourite interview question).
- Streams & TTL β DynamoDB Streams is a 24-hour ordered change-log of every write (change-data-capture); TTL auto-deletes expired items. Both in Part 3.
Here's the shape of the primary key, the single most important modeling decision you'll make:
π‘ Partition key = which aisle your box goes in. Sort key = the order boxes sit on the shelf within that aisle. Pick the partition key to spread load evenly; pick the sort key to match how you'll page through related items.
3. How you actually use it β a short example
DynamoDB is API-driven β there's no SQL by default (though PartiQL, a SQL-like syntax, exists as a convenience layer). You talk to it with a handful of verbs: PutItem, GetItem, Query, Scan, UpdateItem, DeleteItem, plus batch and transactional variants.
Say we're storing orders. We want to look up "all orders for a customer, newest first." That access pattern dictates the key: partition key = customerId, sort key = orderDate.
import boto3
ddb = boto3.resource("dynamodb")
# Create the table: composite key = (customerId, orderDate)
ddb.create_table(
TableName="Orders",
KeySchema=[
{"AttributeName": "customerId", "KeyType": "HASH"}, # partition key
{"AttributeName": "orderDate", "KeyType": "RANGE"}, # sort key
],
AttributeDefinitions=[
{"AttributeName": "customerId", "AttributeType": "S"},
{"AttributeName": "orderDate", "AttributeType": "S"},
],
BillingMode="PAY_PER_REQUEST", # on-demand β no capacity to plan
)
orders = ddb.Table("Orders")
# Write an item β note: only the KEY attributes are mandatory; the rest are free-form
orders.put_item(Item={
"customerId": "cust#42",
"orderDate": "2026-07-05T10:15:00Z",
"total": 129.90,
"items": ["book", "pen"], # a list attribute β no schema needed
})
# The whole point: one query, one partition, pre-sorted, newest first
resp = orders.query(
KeyConditionExpression="customerId = :c",
ExpressionAttributeValues={":c": "cust#42"},
ScanIndexForward=False, # sort key descending β newest first
Limit=20,
)
Notice three things that will define everything that follows:
- The key came from the query, not the entity. We didn't ask "what fields does an order have?" β we asked "how will we read orders?" That's query-first modeling, and it's the whole game (Part 4).
- Only key attributes have a declared type.
totalanditemswere never declared β DynamoDB is schemaless outside the key. Querytargets one partition key. It's cheap and fast because it's a single-aisle lookup. Its dangerous siblingScanreads the entire table and is the classic newbie footgun β avoid it in hot paths. π«
4. When to reach for it β and when not
DynamoDB is the right answer to a shape of problem, the same shape Cassandra fits: high-volume, key-accessed, always-on, latency-sensitive β but with a strong extra pull toward it when you're on AWS and would rather not run a cluster.
Reach for DynamoDB when:
- You want predictable single-digit-ms latency at any scale with zero ops.
- Access is by a known key (get this user, this cart, this session, this device's readings).
- The workload is write-heavy or spiky (shopping carts, sessions, gaming state, IoT, event logs).
- You need always-on availability across AZs, and multi-region with Global Tables.
Look elsewhere when:
| You need⦠| Reach for instead | Why |
|---|---|---|
| Ad-hoc queries, joins, aggregations | Postgres/MySQL, or an OLAP store | DynamoDB has no joins and only key-based access |
| Rich flexible querying on a document model | MongoDB | Secondary access patterns are far cheaper to add |
| Full control / on-prem / not on AWS | Apache Cassandra | Same distribution model, self-managed, cloud-agnostic |
| Big blobs (images, video) | Object storage (S3) | 400 KB item cap; store the blob in S3, the pointer in DynamoDB |
| Strong multi-row transactions as the norm | A relational DB | DynamoDB has transactions, but they're a targeted tool, not the default |
β οΈ The single biggest failure with DynamoDB isn't operational β it's modeling. Because it looks like "just a key-value store," people bolt it onto a relational mindset, then discover they can't run the ad-hoc query the product now needs. DynamoDB rewards teams who know their access patterns up front and punishes teams who don't. If your query patterns are unknown or constantly shifting, that's a signal to reconsider (Part 4 makes this concrete).
5. Key takeaways
- DynamoDB is a fully managed, serverless Dynamo-family key-value/document store β Cassandra's distributed DNA, but Amazon runs partitioning, replication, failover, and scaling for you, promising predictable low latency at any scale.
- The primary key is a physical decision: the partition key (hashed) picks the partition/aisle; the optional sort key orders items within it and unlocks range queries. Everything downstream flows from this choice.
- It's schemaless outside the key, API-driven (not SQL), and cheap only when you query by key β
Queryhits one partition;Scanreads the whole table and is a footgun. - You pay in RCUs/WCUs or on-demand, and you choose consistency (eventual vs strong) per read β the levers we'll quantify in Part 3.
- Reach for it on the Cassandra shape plus "on AWS, don't want to run a cluster." Reach elsewhere when you need joins, ad-hoc queries, or large blobs. The number-one failure mode is modeling without knowing your access patterns.
Next, Part 2 goes behind the warehouse wall: how a table breaks into partitions, how each partition is a Paxos-led replication group across three AZs, and how a write travels from the front desk to durable storage.