Skip to content

1. What DynamoDB is, and why it exists

Every other database in this collection will let you write a query whose cost depends on how much data you have. SELECT … JOIN … GROUP BY is fast on ten thousand rows and ruinous on ten billion, and nothing in the syntax warns you.

DynamoDB’s founding decision is to make that impossible:

Every operation must have a cost that can be predicted before it runs. Anything that cannot be predicted is not offered.

No joins. No aggregate over arbitrary rows. No query the engine cannot price in advance. In exchange, single-digit-millisecond latency that does not change when your table grows from one gigabyte to one petabyte.

Whether that is a brilliant trade or an unacceptable one depends entirely on whether you know your access patterns in advance — and this guide is about being honest about that question before you commit.

Amazon’s 2004 holiday season is the origin story that every account of this database repeats, because it is genuinely the reason it exists. The shopping cart ran on a relational database, and the relational database was the thing that fell over under load.

The lesson they drew was specific:

THE FAILURE WAS NOT CAPACITY
it was that the system's behaviour under load
was UNPREDICTABLE. one expensive query, one
unexpected join plan, one lock convoy, and the
latency distribution fell apart.
WHAT THE CART ACTUALLY NEEDED
get cart by customer id
put cart by customer id
…and nothing else, ever, at any scale

That produced the Dynamo paper in 2007 — consistent hashing, vector clocks, quorum reads and writes, hinted handoff. The paper became one of the most influential systems papers of its decade and is the direct ancestor of Cassandra, Riak and Voldemort.

Then, in 2012, AWS shipped DynamoDB, which is not the Dynamo paper. Werner Vogels has been explicit about this: internal teams found the original Dynamo hard to operate, and what customers actually wanted was for somebody else to run it.

DYNAMO (2007) DYNAMODB (2012)
a paper and an internal a managed service
system you operate
eventually consistent, strong consistency
vector clocks, conflict available per read;
resolution in the client last-writer-wins
quorum tuning is yours invisible; replication
to 3 AZs is a given
you run it you cannot run it. there
is no download.

That last row is the single most consequential fact about the product, and chapter 15 treats it as the strategic decision it is.

GET an item by its key
→ single-digit milliseconds
→ at 1 GB or 100 TB
→ at 10 requests/s or 10,000,000 requests/s
→ with no capacity planning meeting
and the cost is a known number of CAPACITY UNITS,
computed from the size of the item, not from the
size of the table.

Everything else in DynamoDB — the partition key rules, the absence of joins, the 1 MB query limit, the way indexes work — exists to protect that promise. When the product seems to be refusing something reasonable, it is almost always refusing to let you build something whose cost it could not have predicted.

Stated plainly, up front, because the rest of the guide assumes you have accepted it:

✗ JOINS none. you model the join away
or you do it in the application.
✗ AD HOC QUERIES you can only query efficiently by
the keys you designed for. a new
question may need a new index or
a new table.
✗ AGGREGATES no SUM, no GROUP BY over arbitrary
rows. you maintain aggregates as
you write, or compute them in
another system.
✗ PORTABILITY it is an AWS service. there is no
self-hosted version.
✗ FLEXIBILITY the schema is flexible; the ACCESS
PATTERNS are not. that is the exact
opposite of how people first read
"NoSQL".
TABLE
└── ITEMS (like rows, but schemaless
└── ATTRIBUTES beyond the key)
PRIMARY KEY
partition key (PK) decides WHICH PARTITION
sort key (SK), optional decides ORDER WITHIN it
Everything in DynamoDB follows from those two lines.
PK = CUSTOMER#42
┌────────────────────────────────────────────────┐
│ SK = PROFILE {name, email} │
│ SK = ORDER#2026-01-02 {total, status} │
│ SK = ORDER#2026-03-11 {total, status} │
│ SK = ADDRESS#home {city, zip} │
└────────────────────────────────────────────────┘
one partition · sorted by SK · one query
returns any contiguous slice of it

That picture is single-table design (chapter 8), and it is why practitioners talk about DynamoDB so differently from other databases: the relationships are encoded in key structure, so a “join” becomes a single range read of one partition.

Terminal window
aws dynamodb create-table \
--table-name AppData \
--attribute-definitions AttributeName=PK,AttributeType=S \
AttributeName=SK,AttributeType=S \
--key-schema AttributeName=PK,KeyType=HASH \
AttributeName=SK,KeyType=RANGE \
--billing-mode PAY_PER_REQUEST
Terminal window
aws dynamodb put-item --table-name AppData --item '{
"PK": {"S": "CUSTOMER#42"},
"SK": {"S": "PROFILE"},
"name": {"S": "Alice"}
}'
# everything belonging to customer 42, in one request
aws dynamodb query --table-name AppData \
--key-condition-expression 'PK = :pk' \
--expression-attribute-values '{":pk":{"S":"CUSTOMER#42"}}'

For local work there is DynamoDB Local, a Java implementation that speaks the same API — useful for tests, and not a performance or capacity simulator (chapter 12).

CASSANDRA the Dynamo paper's other descendant.
same partition-key thinking, but you
run it, and you tune consistency
yourself. (ch 15)
MONGODB flexible documents AND flexible
queries — you can ask new questions
later, at the cost of predictability.
POSTGRESQL any question, any time; performance
is your problem to discover.
REDIS also key-based and also fast, but in
memory and not durable in the same
sense.
DYNAMODB the question you designed for, at any
scale, for a price you can compute.
1. THE PARTITION KEY DECIDES EVERYTHING. It chooses
the partition, and therefore the machine, the
throughput ceiling and the hot-key risk. → ch 3
2. QUERY IS BY KEY. Query reads one partition by PK,
optionally a range of SK. SCAN reads everything
and is almost always a mistake in the hot path.
3. CAPACITY UNITS ARE THE UNIT OF THOUGHT. Latency is
guaranteed; what you manage is throughput and
cost. → ch 6
4. INDEXES ARE COPIES OF YOUR DATA with different
keys, with their own cost and their own failure
modes. → ch 7
5. MODEL FROM ACCESS PATTERNS, never from entities.
List the queries first; the table falls out of
them. → ch 8

The claim is abstract until you price it. Take one access pattern — “fetch a customer and their ten most recent orders” — at three scales.

1 GB table one index scan + a join. ~2 ms
100 GB table same plan, colder cache. ~5–20 ms
2 TB table the plan may change. the join may
spill. the answer is "it depends,
and you will find out in production".

Nothing about the SQL changed. The performance did, because the cost of the plan is a function of data volume and cache state.

1 GB table Query PK=CUSTOMER#42, 11 items.
~5 ms, 1 RCU.
100 GB table identical. ~5 ms, 1 RCU.
2 TB table identical. ~5 ms, 1 RCU.

The reason is structural: the request hashes to one partition and reads a contiguous, sorted range (chapter 3). Nothing in that path scales with table size.

"which customers spent over €1,000 last month?"
POSTGRESQL a SELECT with a GROUP BY. slow,
perhaps, but it EXISTS.
DYNAMODB not a query. your options are:
• maintain the aggregate as you
write (ch 9, 10)
• add an index you designed for
this in advance (ch 7)
• export to S3 and ask there (ch 11)
• scan the table and pay for it

That is the trade, complete. Predictable cost for the questions you planned; a project for the ones you did not. Everything in chapters 7 through 11 is machinery for living well inside it.

DynamoDB is the default database of serverless AWS architectures, which means a great many engineers meet it not because they chose it but because it was already there. Understanding its rules is the difference between a service that costs forty dollars a month and one that costs four thousand for the same traffic.

It is also the clearest example in this collection of a system that gets its strength from what it refuses to do. Every constraint in the chapters ahead is deliberate, and once you can name the reason for each one, the database stops feeling arbitrary and starts feeling — like Amazon’s shopping cart — extremely narrow and extremely reliable.

  • The founding rule: every operation must have a predictable cost, so anything unpredictable — joins, ad hoc queries, arbitrary aggregates — is simply not offered.
  • It descends from the 2007 Dynamo paper but is not that system: DynamoDB (2012) is managed, strongly consistent on request, and impossible to self-host.
  • The promise is single-digit-millisecond access by key at any table size, priced in capacity units computed from item size rather than table size.
  • What you give up: joins, ad hoc queries, aggregates, portability — and, crucially, flexibility of access patterns, which is the opposite of what “schemaless” suggests.
  • The whole model is partition key (which partition) plus sort key (order within it), which is what makes single-table design possible.
  • Carry five ideas: the partition key decides everything, query by key and never scan in the hot path, capacity units are the unit of thought, indexes are copies with their own costs, and model from access patterns, not entities.

Next: tables, keys and items — the data model in detail, and the limits that shape every design decision that follows.

Written by Sepuri Sai Krishna

© 2026CC BY 4.0Source