DynamoDB Core Vocabulary
coreintermediateA table holds items (rows); each item holds attributes (columns), schemaless beyond the primary key. A partition key alone must be unique per item; a composite key (partition + sort) lets multiple items share a partition key, sorted by sort key. A Query fetches by key efficiently; a Scan reads the whole table and is expensive at any real size. A GSI has its own partition/sort key (queryable independent of the base table's key); an LSI shares the base table's partition key with a different sort key, and must be created at table creation time.
Think of it as
The partition key is which filing cabinet drawer an item lives in; the sort key is where within that drawer it sits, kept in order. A Query says "open this specific drawer (optionally a range within it)" — fast and cheap. A Scan says "open every drawer in the building and look through all of them" — thorough but expensive at scale.
What we're doing: See why a Scan with a filter still reads (and pays for) the whole table.
- 1
- The filter expression looks like a WHERE clause, but it is applied client-side of the read, not as part of it.
- 3
- Every one of the 10 million items is read and billed before the filter ever discards a single non-matching one — this is the defining cost characteristic of a Scan.
Why this works: A FilterExpression on a Scan is a post-read filter, not an index lookup — this is precisely why DynamoDB's own guidance is to design access patterns around Query wherever possible, and treat Scan as an operational or small-table-only tool.
Using Scan with a filter as a substitute for designing a proper access pattern
Wrong
Better
What you see: Read costs and latency scale with total table size rather than result size, and the problem gets worse every month as the table grows — even though the query itself always returns a small, similar-sized result.
Why: DynamoDB is explicitly designed around access-patterns-first modeling — a Scan-with-filter approach defers that design work, but the cost is not deferred, it accrues linearly with table growth regardless of how selective the filter actually is.
- Query
- Uses the key structure directly
- Reads only the matching partition (+ sort range)
- Cheap and fast at any table size
- Scan
- Reads the entire table or index
- Filters happen after the read, not before
- Cost grows with table size, regardless of result size
Remember: Partition key alone must be unique; composite key lets the partition repeat, pair must be unique. Query uses the key structure (cheap); Scan reads everything then filters (expensive at scale). GSI: own key, addable anytime. LSI: shares the base partition key, must be defined at table creation.
See also: single table design and modeling · hot partitions and adaptive capacity

