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

ConceptDescription
TableCollection of items
ItemA row (JSON document, max 400 KB)
AttributeA field in an item
Partition Key (PK)Primary key — determines data distribution
Sort Key (SK)Optional — enables range queries within a partition
GSIGlobal Secondary Index — query on non-key attributes
LSILocal 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

ModeDescriptionBest For
On-DemandPay per request, auto-scalesUnpredictable workloads
ProvisionedSet RCU/WCU, use auto-scalingPredictable 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