Amazon DynamoDB: Key-Value and Document Database Built for Any Scale
DynamoDB solves a problem that relational databases handle poorly: maintaining consistent latency as data volume and request rate grow by orders of magnitude. A well-designed relational database might perform well at 10,000 requests per second. At 10 million requests per second, it needs specialized sharding, caching layers, read replicas, and significant operational investment. DynamoDB is designed to deliver single-digit millisecond latency at 10 million requests per second with the same configuration as at 100 requests per second.
The trade-off is the data model. DynamoDB is not a drop-in replacement for PostgreSQL. It requires upfront thinking about access patterns, and it does not support arbitrary SQL queries across the whole table.
The Data Model
DynamoDB stores items in tables. Every item has a primary key; everything else is optional and can vary between items.
Partition key only: the primary key is just the partition key. DynamoDB hashes the partition key to determine which partition stores the item. Every item’s partition key must be unique.
Partition key + sort key: the primary key is a composite of partition key and sort key. Items with the same partition key are stored together, sorted by sort key. This allows efficient range queries: “give me all orders for customer C1234 placed after January 1st.”
DynamoDB Table: Orders ======================
Partition Key: CustomerId Sort Key: OrderDate
CustomerId OrderDate Amount Status ────────── ────────── ────── ────── C1234 2025-01-15 $89.99 shipped C1234 2025-03-22 $245.00 pending C1234 2025-06-01 $12.50 delivered C5678 2025-02-10 $500.00 shipped C5678 2025-05-18 $67.25 cancelled
Query: "all orders for C1234 in 2025" — efficient, reads only C1234's partition Scan: "all orders over $100" — reads entire table, expensive, avoidThe data model forces you to think about access patterns first. What queries will run most often? Design the primary key to make those queries fast. Use indexes for secondary access patterns.
Capacity Modes
DynamoDB offers two billing and capacity models:
Provisioned capacity: you specify read and write capacity units (RCUs and WCUs) that DynamoDB reserves for your table. One RCU supports one strongly consistent read of an item up to 4 KB per second. One WCU supports one write of an item up to 1 KB per second. If you exceed the provisioned capacity, requests are throttled. Auto Scaling can adjust capacity based on CloudWatch metrics, but there is inherent lag.
On-demand capacity: you pay per request rather than reserving capacity. DynamoDB instantly accommodates any request rate, up to the table’s previous peak traffic doubled. Better for unpredictable traffic, new tables, and applications where over-provisioning waste is unacceptable. More expensive per request than well-optimized provisioned capacity.
Read Consistency
DynamoDB offers two consistency options per read:
Eventually consistent reads (default): the response might not reflect very recent writes. DynamoDB might serve data from any of the multiple copies. Costs 0.5 RCU per 4 KB.
Strongly consistent reads: guarantees the response reflects all writes that completed before the read. Always reads from the primary copy. Costs 1 RCU per 4 KB and has slightly higher latency.
Most applications can tolerate eventual consistency. Use strongly consistent reads when your application needs to read data it just wrote and cannot tolerate stale results — for example, after updating account balance, reading it back to display to the user.
Secondary Indexes
Tables are queried efficiently only by their primary key. Secondary indexes enable efficient queries on other attributes:
Local Secondary Index (LSI): same partition key as the table, different sort key. Can only be created at table creation time. Shares throughput with the base table.
Global Secondary Index (GSI): different partition key and sort key from the base table. Can be created at any time. Has its own provisioned throughput. Used for access patterns that need to look up items by an attribute that is not the partition key.
When to Use Each Index Type ===========================
Orders table: PK = CustomerId, SK = OrderDate
Need: find all orders with status "pending" → GSI GSI PK = Status, GSI SK = OrderDate Query: Status = "pending", sort by date
Need: find orders for customer C1234 sorted by Amount → LSI LSI PK = CustomerId (same), LSI SK = Amount (different) Query: CustomerId = "C1234", sort by Amount
LSI must be created at table creation GSI can be added to existing tableDynamoDB Accelerator (DAX)
DAX is a fully managed, in-memory cache specifically for DynamoDB. It sits in front of the table and serves reads from memory with microsecond latency — 10-100x faster than standard DynamoDB reads. Applications use the DAX SDK client, which routes reads to DAX and falls through to DynamoDB only on cache misses. Writes go through DAX to DynamoDB.
Use DAX when you need the absolute lowest read latency and your workload is read-heavy. It is not appropriate for write-heavy workloads or when strong consistency is required on all reads (DAX returns cached, potentially stale data).
Real-World Use Case: Ride-Sharing App
A ride-sharing application uses DynamoDB for several tables:
Active rides table: partition key = rideId. Items are created when a ride starts, updated every few seconds with driver location, deleted when the ride ends. This table is small but extremely write-heavy. On-demand capacity absorbs traffic spikes during rush hour without over-provisioning.
User ride history: partition key = userId, sort key = rideDate. Riders can query all their rides or rides within a date range. A GSI on driverId + rideDate lets the operations team audit a specific driver’s history.
Promotions table: partition key = promoCode. Lookup is always by code. DAX caches all active promotions — millions of rides per day check for a discount code, and the promotion catalog changes infrequently. DAX reduces read costs and latency for this high-traffic path.
Key Interview Points
- Partition key design is critical: items with the same partition key go to the same partition. If one partition key gets disproportionate traffic (a “hot partition”), you hit throughput limits on that partition even if overall table capacity is sufficient. Design partition keys that distribute traffic evenly.
- DynamoDB is not relational: no joins, no arbitrary WHERE clauses across non-indexed attributes, no stored procedures — if you need those, use RDS or Aurora
- Scan is expensive: a full table scan reads every item regardless of filters applied afterward. In production, Scan should be a rare operation on small tables or replaced by a properly designed query
- TTL (Time to Live): set an attribute as TTL and DynamoDB automatically deletes items when the timestamp expires — no cron jobs needed for session cleanup or expired tokens
- DynamoDB Streams: captures a time-ordered log of item changes — enables event-driven architectures (Lambda triggered on writes) and cross-region replication via Global Tables
- Item size limit: 400 KB maximum per item — store large objects in S3 and reference them from DynamoDB
- Conditional writes:
ConditionExpressionallows writes only if a condition is met — the basis for optimistic locking patterns