Docs
/
AWS Cloud
Chapter 7
07 — DynamoDB
What is DynamoDB?
Fully managed NoSQL database — single-digit millisecond performance at any scale. Serverless, no provisioning.
Core Concepts
| Concept | Description |
|---|---|
| Table | Collection of items |
| Item | A row (JSON document, max 400 KB) |
| Attribute | A field in an item |
| Partition Key (PK) | Primary key — determines data distribution |
| Sort Key (SK) | Optional — enables range queries within a partition |
| GSI | Global Secondary Index — query on non-key attributes |
| LSI | Local Secondary Index — alternate sort key (same partition key) |
Key Design
Table: Users
PK: userId
Table: Orders
PK: userId SK: orderId
→ Get all orders for a user (Query on PK)
→ Get specific order (PK + SK)
Table: SingleTable
PK: "USER#123" SK: "PROFILE" → User profile
PK: "USER#123" SK: "ORDER#2024-001" → User's order
PK: "PRODUCT#abc" SK: "METADATA" → Product info
Operations
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, PutCommand, GetCommand, QueryCommand, DeleteCommand } from '@aws-sdk/lib-dynamodb';
const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));
// Put item
await client.send(new PutCommand({
TableName: 'Orders',
Item: {
userId: 'user-123',
orderId: 'order-001',
total: 49.99,
status: 'pending',
createdAt: new Date().toISOString(),
},
}));
// Get item (by exact PK + SK)
const { Item } = await client.send(new GetCommand({
TableName: 'Orders',
Key: { userId: 'user-123', orderId: 'order-001' },
}));
// Query (all orders for a user)
const { Items } = await client.send(new QueryCommand({
TableName: 'Orders',
KeyConditionExpression: 'userId = :uid AND begins_with(orderId, :prefix)',
ExpressionAttributeValues: {
':uid': 'user-123',
':prefix': 'order-',
},
}));
// Delete item
await client.send(new DeleteCommand({
TableName: 'Orders',
Key: { userId: 'user-123', orderId: 'order-001' },
}));
Capacity Modes
| Mode | Description | Best For |
|---|---|---|
| On-Demand | Pay per request, auto-scales | Unpredictable workloads |
| Provisioned | Set RCU/WCU, use auto-scaling | Predictable workloads (cheaper) |
RCU (Read Capacity Unit):
1 RCU = 1 strongly consistent read/sec (up to 4 KB)
1 RCU = 2 eventually consistent reads/sec
WCU (Write Capacity Unit):
1 WCU = 1 write/sec (up to 1 KB)
Global Secondary Index (GSI)
Query on attributes other than the primary key.
Table: Orders (PK: userId, SK: orderId)
GSI: StatusIndex (PK: status, SK: createdAt)
→ Query all "pending" orders sorted by date
→ Query all "shipped" orders from last week
const { Items } = await client.send(new QueryCommand({
TableName: 'Orders',
IndexName: 'StatusIndex',
KeyConditionExpression: '#s = :status',
ExpressionAttributeNames: { '#s': 'status' },
ExpressionAttributeValues: { ':status': 'pending' },
}));
Single-Table Design
Store multiple entity types in one table — reduces joins and costs.
PK SK Data
──────────── ────────────── ─────────────────
USER#123 PROFILE { name, email }
USER#123 ORDER#001 { total, status }
USER#123 ORDER#002 { total, status }
PRODUCT#abc METADATA { name, price }
PRODUCT#abc REVIEW#r1 { rating, text }
GSI1PK GSI1SK
──────────── ──────────────
ORDER#001 USER#123 (lookup order → user)
pending 2024-01-15 (query by status)
DynamoDB Streams
Capture item-level changes (insert, update, delete) → trigger Lambda.
Table change → DynamoDB Stream → Lambda
→ Update search index
→ Send notification
→ Replicate to another table
DAX (DynamoDB Accelerator)
In-memory cache in front of DynamoDB — microsecond reads.
App → DAX (cache) → DynamoDB
Hit: ~microseconds
Miss: ~milliseconds (reads from DynamoDB, caches result)
Key Takeaways
- DynamoDB = serverless NoSQL — millisecond performance at any scale
- Design keys for access patterns — PK for partitioning, SK for range queries
- GSI for querying on non-key attributes (most tables need at least one)
- Single-table design reduces cost and complexity for related entities
- On-Demand for variable traffic; Provisioned for predictable (cheaper)
- Streams + Lambda for event-driven reactions to data changes
- Use DAX for microsecond read-heavy caching