What is DynamoDB?

Fully managed NoSQL database (key-value and document). Serverless - no instances to manage.

Key Terms

TermWhat it is
TableCollection of items (like a table in SQL, but schema-less)
ItemSingle record (like a row in SQL)
AttributeField within an item (each item can have different attributes)
Partition Key (PK)Primary identifier - DynamoDB uses this to distribute data
Sort Key (SK)Optional secondary identifier - enables range queries within a partition
Primary KeyEither PK alone, or PK + SK combined
┌─────────────────────────────────────────────────────────────────┐
│ DynamoDB Table: Orders                                          │
│                                                                 │
│ Primary Key: customer_id (PK) + order_date (SK)                 │
│                                                                 │
│ ┌─────────────┬─────────────┬──────────┬───────────┐           │
│ │ customer_id │ order_date  │ total    │ items     │           │
│ │ (PK)        │ (SK)        │          │           │           │
│ ├─────────────┼─────────────┼──────────┼───────────┤           │
│ │ user123     │ 2026-01-01  │ 150.00   │ [...]     │           │
│ │ user123     │ 2026-01-02  │ 75.50    │ [...]     │           │
│ │ user456     │ 2026-01-01  │ 200.00   │ [...]     │           │
│ └─────────────┴─────────────┴──────────┴───────────┘           │
│                                                                 │
│ Query: Get all orders for user123 → returns 2 items             │
│ Query: Get user123's orders after 2026-01-01 → returns 1 item   │
└─────────────────────────────────────────────────────────────────┘

RDS/Aurora vs DynamoDB

AspectRDS/AuroraDynamoDB
TypeRelational (SQL)NoSQL (key-value)
SchemaFixed (define columns upfront)Flexible (each item can differ)
ScalingVertical (bigger instance)Horizontal (automatic partitioning)
QueriesAny SQL query (JOINs, etc.)Limited (by key only, no JOINs)
TransactionsFull ACIDLimited ACID (up to 100 items)
ManagementYou manage instance sizeFully serverless
PricingPer instance hourPer request + storage

What “Manage Instance Size” Means (RDS/Aurora)

DB instance (EC2) size - the compute. You choose db.r5.large, db.r5.2xlarge, etc.

Storage is separate:

  • RDS: You provision EBS size (e.g., 100GB, can grow)
  • Aurora: Storage auto-grows (no provisioning needed)
RDS: You manage both
┌─────────────────┐     ┌─────────────────┐
│ DB Instance     │     │ EBS Volume      │
│ (you choose     │────►│ (you provision  │
│  db.r5.large)   │     │  100GB)         │
└─────────────────┘     └─────────────────┘

Aurora: You manage compute only
┌─────────────────┐     ┌─────────────────┐
│ DB Instance     │     │ Shared Storage  │
│ (you choose     │────►│ (auto-grows,    │
│  db.r5.large)   │     │  AWS manages)   │
└─────────────────┘     └─────────────────┘

RDS EBS resize: Online, no downtime. But 6-hour cooldown between resizes, can only increase.


DynamoDB Architecture

Completely different from RDS/Aurora - no instances, no storage you see.

┌─────────────────────────────────────────────────────────────────┐
│ DynamoDB (what you see)                                         │
│                                                                 │
│   Just a table with items. No instances, no storage config.     │
│   You only configure: capacity mode (on-demand or provisioned)  │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│ DynamoDB (what AWS runs - hidden from you)                      │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │ Request Router                                          │   │
│   │ (receives your API calls)                               │   │
│   └────────────────────────┬────────────────────────────────┘   │
│                            │                                    │
│              ┌─────────────┼─────────────┐                      │
│              ▼             ▼             ▼                      │
│   ┌──────────────┐ ┌──────────────┐ ┌──────────────┐           │
│   │ Partition 1  │ │ Partition 2  │ │ Partition 3  │           │
│   │ (PK hash     │ │ (PK hash     │ │ (PK hash     │           │
│   │  0-33%)      │ │  34-66%)     │ │  67-100%)    │           │
│   │              │ │              │ │              │           │
│   │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │           │
│   │ │ Storage  │ │ │ │ Storage  │ │ │ │ Storage  │ │           │
│   │ │ Node     │ │ │ │ Node     │ │ │ │ Node     │ │           │
│   │ └──────────┘ │ │ └──────────┘ │ │ └──────────┘ │           │
│   │ (3 replicas  │ │ (3 replicas  │ │ (3 replicas  │           │
│   │  across AZs) │ │  across AZs) │ │  across AZs) │           │
│   └──────────────┘ └──────────────┘ └──────────────┘           │
│                                                                 │
│   AWS adds/removes partitions automatically based on:           │
│   - Data size (10GB per partition)                              │
│   - Throughput needs                                            │
└─────────────────────────────────────────────────────────────────┘

Architecture Comparison

RDSAuroraDynamoDB
ComputeYou choose instanceYou choose instanceHidden (serverless)
StorageYou provision EBSAuto-grows (shared)Hidden (auto-partitioned)
Scaling computeManualManualAutomatic
Scaling storageManualAutomaticAutomatic
You seeInstances + storageInstances + storageJust a table
PricingInstance hours + storageInstance hours + storage + I/ORequests + storage

Replication & High Availability

FeatureWhat it does
Built-in replicationEvery item stored in 3 AZs (always on, no config)
Global TablesMulti-region active-active replication
Single Region (automatic):
┌─────────────────────────────────────────────────────────────────┐
│ us-east-1                                                       │
│   ┌─────────┐     ┌─────────┐     ┌─────────┐                   │
│   │ AZ-a    │     │ AZ-b    │     │ AZ-c    │                   │
│   │ Replica │◄───►│ Replica │◄───►│ Replica │                   │
│   └─────────┘     └─────────┘     └─────────┘                   │
│                                                                 │
│   Write acknowledged after 2 of 3 replicas confirm             │
└─────────────────────────────────────────────────────────────────┘

Global Tables (you enable):
┌─────────────────────┐     ┌─────────────────────┐
│ us-east-1           │     │ eu-west-1           │
│ ┌─────────────────┐ │     │ ┌─────────────────┐ │
│ │ Table (R/W)     │◄┼─────┼►│ Table (R/W)     │ │
│ └─────────────────┘ │     │ └─────────────────┘ │
└─────────────────────┘     └─────────────────────┘
        │                           │
        └─── Both regions can ──────┘
             read AND write
             (active-active)

Active-Passive vs Active-Active

ModeMeaning
Active-PassiveOne region handles writes, other is read-only standby
Active-ActiveBoth regions can handle writes simultaneously
Active-Passive (Aurora Global Database):
┌─────────────────────┐     ┌─────────────────────┐
│ us-east-1 (Primary) │     │ eu-west-1 (Secondary)│
│                     │     │                     │
│  Read ✓  Write ✓    │────►│  Read ✓  Write ✗    │
│                     │     │  (read-only)        │
└─────────────────────┘     └─────────────────────┘

Active-Active (DynamoDB Global Tables):
┌─────────────────────┐     ┌─────────────────────┐
│ us-east-1           │     │ eu-west-1           │
│                     │     │                     │
│  Read ✓  Write ✓    │◄───►│  Read ✓  Write ✓    │
│                     │     │                     │
└─────────────────────┘     └─────────────────────┘

Why Aurora Can’t Be Active-Active

Relational DBs need strong consistency. Conflict resolution is complex with transactions, foreign keys, constraints.

Problem:
User in US: UPDATE accounts SET balance = 100 WHERE id = 1
User in EU: UPDATE accounts SET balance = 200 WHERE id = 1
(same row, same time)

Which one wins? Aurora's solution: Only one region accepts writes.

Why DynamoDB Can Be Active-Active

DynamoDB handles conflicts with “last writer wins” (timestamp-based):

User in US: item.status = "shipped"   (timestamp: 100)
User in EU: item.status = "delivered" (timestamp: 101)

Both regions eventually have: status = "delivered" (higher timestamp wins)

Works because: No JOINs, no foreign keys, each item is independent.


Capacity Modes

ModeHow it worksBest for
On-DemandPay per request, auto-scalesUnpredictable traffic, new apps
ProvisionedYou set RCU/WCU, pay for capacityPredictable traffic, cost optimization
On-Demand:
  Traffic spikes → DynamoDB handles it → Pay for what you used

Provisioned:
  You set: 100 RCU, 50 WCU
  Traffic exceeds → Throttled (unless Auto Scaling enabled)
  Pay for provisioned capacity even if unused

RCU/WCU:

  • RCU (Read Capacity Unit) = 1 strongly consistent read/sec (up to 4KB)
  • WCU (Write Capacity Unit) = 1 write/sec (up to 1KB)

Secondary Indexes

Query data by attributes other than primary key.

Index TypeWhat it doesLimit
GSI (Global Secondary Index)New partition key + optional sort key20 per table
LSI (Local Secondary Index)Same partition key, different sort key5 per table, must create at table creation
Table: Orders
  PK: customer_id
  SK: order_date

Problem: "Find all orders with status=shipped"
  → Can't query by status (not in primary key)

Solution: GSI
  GSI PK: status
  GSI SK: order_date
  → Now can query by status

DynamoDB Streams

Capture item-level changes (insert, update, delete) in real-time.

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│ DynamoDB Table  │────►│ DynamoDB Stream │────►│ Lambda          │
│                 │     │ (24hr retention)│     │ (process changes│
│ INSERT/UPDATE/  │     │                 │     │  in real-time)  │
│ DELETE          │     │                 │     │                 │
└─────────────────┘     └─────────────────┘     └─────────────────┘

Use cases:

  • Trigger Lambda on data changes
  • Replicate to other systems (Elasticsearch, analytics)
  • Audit log
  • Cross-region replication (Global Tables uses this internally)

TTL (Time to Live)

Automatically delete expired items. Free (no WCU consumed).

{
  "user_id": "123",
  "session_token": "abc...",
  "expires_at": 1704153600    // Unix timestamp - deleted after this
}

Use cases: Sessions, temporary data, logs with retention.


Backup & Recovery

TypeWhat it does
On-demand backupManual snapshot, kept until you delete
PITR (Point-in-Time Recovery)Continuous backup, restore to any second in 35 days
AWS BackupCentralized backup management across services
PITR Timeline:
─────────────────────────────────────────────────────────►
     │                    │                    │
  35 days ago          Bad write            Now
                       happened
                          │
                          ▼
              Restore to 1 second before bad write

DAX (DynamoDB Accelerator)

In-memory cache in front of DynamoDB. Microsecond latency.

Without DAX:
App → DynamoDB (1-10ms)

With DAX:
App → DAX Cache (microseconds) → DynamoDB (cache miss only)

When to use: Read-heavy workloads needing sub-millisecond latency.

DAX vs ElastiCache

DAXElastiCache
ForDynamoDB onlyAny database (RDS, Aurora, etc.)
IntegrationDrop-in (same DynamoDB API)You write caching logic
EnginesPurpose-built for DynamoDBRedis or Memcached
You manageCluster sizeCluster size, nodes

ElastiCache: Managed in-memory cache service (Redis or Memcached). Use for RDS/Aurora caching, session storage, leaderboards, real-time analytics.

With ElastiCache (for RDS/Aurora):
┌─────────┐     ┌─────────────┐     ┌─────────────┐
│ App     │────►│ ElastiCache │────►│ Aurora/RDS  │
│         │◄────│ (fast,      │     │ (only cache │
│         │     │  in-memory) │     │  misses)    │
└─────────┘     └─────────────┘     └─────────────┘

Transactions

ACID transactions across multiple items (up to 100 items, 4MB).

# Transfer money: debit one account, credit another (atomic)
client.transact_write_items(
    TransactItems=[
        {'Update': {'TableName': 'Accounts', 'Key': {'id': 'A'}, 
                    'UpdateExpression': 'SET balance = balance - :amt'}},
        {'Update': {'TableName': 'Accounts', 'Key': {'id': 'B'}, 
                    'UpdateExpression': 'SET balance = balance + :amt'}}
    ]
)
# Both succeed or both fail

Encryption

TypeDetails
At restAlways on (AWS owned key, or your KMS key)
In transitHTTPS (always)

Feature Comparison: Aurora vs DynamoDB

FeatureAuroraDynamoDB
Built-in HA6 copies across 3 AZs3 copies across 3 AZs
Cross-regionGlobal DatabaseGlobal Tables
Cross-region modeActive-passiveActive-active
Point-in-time recoveryYes (35 days)Yes (35 days)
CachingElastiCache (separate)DAX (integrated)
Change captureBinlog/CDCStreams
Auto-scaling storageYesYes
Auto-scaling computeNo (manual instance change)Yes (on-demand mode)

When to Use DynamoDB vs RDS/Aurora

Use DynamoDB when:

  • Simple access patterns (key-value lookups)
  • Need massive scale (millions of requests/sec)
  • Want serverless (no instance management)
  • Unpredictable traffic
  • Need active-active multi-region

Use RDS/Aurora when:

  • Complex queries (JOINs, aggregations)
  • Need full SQL
  • Existing relational schema
  • Strong consistency requirements
  • Complex transactions