Skip to content
intermediate Phase 3 · AWS Databases

DynamoDB NoSQL

Build serverless NoSQL applications with DynamoDB tables and indexes.

1h 30m
0 problems
Topic Progress 0%

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'