DynamoDB Fundamentals
DynamoDB Fundamentals
Amazon DynamoDB is a fully managed NoSQL database providing single-digit millisecond performance at any scale.
Core Concepts
| Concept | Description |
|---|---|
| Table | Collection of items |
| Item | A group of attributes (like a row) |
| Attribute | A key-value pair (like a column) |
| Partition Key | Primary key for distribution |
| Sort Key | Optional, enables range queries |
Create a Table
# Simple table with partition key
aws dynamodb create-table \
--table-name Users \
--attribute-definitions \
AttributeName=UserId,AttributeType=S \
--key-schema AttributeName=UserId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
# Table with partition and sort key
aws dynamodb create-table \
--table-name Orders \
--attribute-definitions \
AttributeName=UserId,AttributeType=S \
AttributeName=OrderId,AttributeType=S \
--key-schema \
AttributeName=UserId,KeyType=HASH \
AttributeName=OrderId,KeyType=RANGE \
--billing-mode PAY_PER_REQUEST
# Describe table
aws dynamodb describe-table --table-name Users
# List tables
aws dynamodb list-tables
Key Design Principles
Partition Key (PK) determines:
- Which partition stores the data
- Even distribution across partitions
- Access pattern for queries
Sort Key (SK) enables:
- Range queries (Between, begins_with)
- Sorting within a partition
- Composite queries
CRUD Operations
CRUD Operations
Put Item
# Insert an item
aws dynamodb put-item \
--table-name Users \
--item '{
"UserId": {"S": "user-123"},
"Name": {"S": "John Doe"},
"Email": {"S": "john@example.com"},
"Age": {"N": "30"},
"IsActive": {"BOOL": true},
"Tags": {"SS": ["premium", "active"]}
}'
# Conditional put (only if doesn't exist)
aws dynamodb put-item \
--table-name Users \
--item '{
"UserId": {"S": "user-123"},
"Name": {"S": "John Doe"}
}' \
--condition-expression "attribute_not_exists(UserId)"
Get Item
# Get a single item
aws dynamodb get-item \
--table-name Users \
--key '{"UserId": {"S": "user-123"}}'
# Get with projection
aws dynamodb get-item \
--table-name Users \
--key '{"UserId": {"S": "user-123"}}' \
--projection-expression "UserId, Name, Email"
Query
# Query by partition key
aws dynamodb query \
--table-name Orders \
--key-condition-expression "UserId = :uid" \
--expression-attribute-values '{":uid": {"S": "user-123"}}'
# Query with sort key
aws dynamodb query \
--table-name Orders \
--key-condition-expression "UserId = :uid AND OrderId > :start" \
--expression-attribute-values '{
":uid": {"S": "user-123"},
":start": {"S": "2024-01-01"}
}'
# Query with filter
aws dynamodb query \
--table-name Orders \
--key-condition-expression "UserId = :uid" \
--filter-expression "Amount > :min" \
--expression-attribute-values '{
":uid": {"S": "user-123"},
":min": {"N": "100"}
}'
Update Item
# Update attributes
aws dynamodb update-item \
--table-name Users \
--key '{"UserId": {"S": "user-123"}}' \
--update-expression "SET Age = :age, Email = :email" \
--expression-attribute-values '{
":age": {"N": "31"},
":email": {"S": "john.doe@example.com"}
}'
# Add to a list
aws dynamodb update-item \
--table-name Users \
--key '{"UserId": {"S": "user-123"}}' \
--update-expression "SET Tags = list_append(if_not_exists(Tags, :empty), :newTags)" \
--expression-attribute-values '{
":newTags": {"SS": ["newtag"]},
":empty": {"L": []}
}'
Delete Item
aws dynamodb delete-item \
--table-name Users \
--key '{"UserId": {"S": "user-123"}}'
Single-Table Design
Single-Table Design
Single-table design stores multiple entity types in one table with composite keys.
Entity Relationship Pattern
┌─────────────────────────────────────────────────────────┐
│ Orders Table │
├──────────────┬──────────────────┬───────────────────────┤
│ PK │ SK │ Attributes │
├──────────────┼──────────────────┼───────────────────────┤
│ USER#123 │ PROFILE │ Name, Email │
│ USER#123 │ ORDER#2024-001 │ Amount, Status │
│ USER#123 │ ORDER#2024-002 │ Amount, Status │
│ ORDER#2024-001 │ METADATA │ UserId, Amount │
│ PRODUCT#456 │ DETAILS │ Name, Price, Stock │
│ PRODUCT#456 │ REVIEW#user-123 │ Rating, Comment │
└──────────────┴──────────────────┴───────────────────────┘
Access Patterns
# Get user profile
aws dynamodb query \
--table-name SingleTable \
--key-condition-expression "PK = :pk AND SK = :sk" \
--expression-attribute-values '{
":pk": {"S": "USER#123"},
":sk": {"S": "PROFILE"}
}'
# Get all orders for a user
aws dynamodb query \
--table-name SingleTable \
--key-condition-expression "PK = :pk AND begins_with(SK, :prefix)" \
--expression-attribute-values '{
":pk": {"S": "USER#123"},
":prefix": {"S": "ORDER#"}
}'
# Get order by order ID (GSI needed)
aws dynamodb query \
--table-name SingleTable \
--index-name GSI1 \
--key-condition-expression "GSI1PK = :pk AND begins_with(GSI1SK, :prefix)" \
--expression-attribute-values '{
":pk": {"S": "ORDER#2024-001"},
":prefix": {"S": "ORDER#"}
}'
Global Secondary Index (GSI)
# Create GSI
aws dynamodb update-table \
--table-name SingleTable \
--attribute-definitions \
AttributeName=GSI1PK,AttributeType=S \
AttributeName=GSI1SK,AttributeType=S \
--global-secondary-index-updates '[{
"Create": {
"IndexName": "GSI1",
"KeySchema": [
{"AttributeName": "GSI1PK", "KeyType": "HASH"},
{"AttributeName": "GSI1SK", "KeyType": "RANGE"}
],
"Projection": {"ProjectionType": "ALL"}
}
}]
DynamoDB Streams and Global Tables
DynamoDB Streams and Global Tables
DynamoDB Streams
Capture item-level modifications for event-driven architectures.
# Enable stream
aws dynamodb update-table \
--table-name Users \
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
# Get shard iterator
aws dynamodb get-shard-iterator \
--table-name Users \
--shard-id shardId-000000000000 \
--shard-iterator-type TRIM_HORIZON
# Read stream records
aws dynamodb get-records --shard-iterator <iterator>
Stream Record Types
| Type | Description |
|---|---|
| KEYS_ONLY | Only key attributes |
| NEW_IMAGE | Item after modification |
| OLD_IMAGE | Item before modification |
| NEW_AND_OLD_IMAGES | Both before and after |
Global Tables
Multi-region, multi-active replication.
# Enable global tables
aws dynamodb update-table \
--table-name Users \
--global-secondary-index-updates '[]' \
--replicas '[{
"RegionName": "eu-west-1",
"ProvisionedThroughputOverride": {
"ReadCapacityUnits": 10,
"WriteCapacityUnits": 5
}
}, {
"RegionName": "ap-southeast-1"
}]'
# Check global table status
aws dynamodb describe-table --table-name Users --region eu-west-1
Stream + Lambda Pattern
DynamoDB Write → Stream → Lambda → Process/Transform → Another Service
Example:
Order Created → Stream → Lambda → Send Email + Update Inventory
DynamoDB Performance and Cost
DynamoDB Performance and Cost
Capacity Modes
| Mode | Pricing | Best For |
|---|---|---|
| On-Demand | Per request | Unpredictable traffic |
| Provisioned | Per capacity unit | Predictable traffic |
Provisioned Throughput
# Update provisioned throughput
aws dynamodb update-table \
--table-name Users \
--provisioned-throughput ReadCapacityUnits=25,WriteCapacityUnits=25
# Auto Scaling
aws application-autoscaling register-scalable-target \
--service-namespace dynamodb \
--resource-id table/Users \
--scalable-dimension dynamodb:table:ReadCapacityUnits \
--min-capacity 5 \
--max-capacity 1000
aws application-autoscaling put-scaling-policy \
--service-namespace dynamodb \
--scalable-dimension dynamodb:table:ReadCapacityUnits \
--resource-id table/Users \
--policy-name ReadAutoScaling \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration '{
"TargetValue": 70.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "DynamoDBReadCapacityUtilization"
}
}'
DynamoDB Accelerator (DAX)
In-memory cache for DynamoDB (microsecond latency).
# Create DAX cluster
aws dynamodb create-cluster \
--cluster-name my-dax-cluster \
--node-type dax.r5.large \
--replication-factor 3 \
--iam-role-arn arn:aws:iam::xxx:role/dax-role \
--subnet-group-name my-subnet-group
# Connect via DAX endpoint
# endpoint: my-dax-cluster.xxx.dax-clusters.us-east-1.amazonaws.com:8111
Cost Optimization
- Use on-demand for dev/test
- Use provisioned with auto-scaling for production
- Enable DAX for read-heavy workloads
- Use sparse indexes to reduce index size
- Archive old data to S3 with Time to Live (TTL)
# Enable TTL
aws dynamodb update-time-to-live \
--table-name Users \
--time-to-live-specification 'Enabled=true, AttributeName=ExpiresAt'