Skip to main content

Amazon DynamoDB

Exam focus: DynamoDB is one of the top 3 most tested services in DVA-C02. Know key design, capacity, and consistency options cold.


๐Ÿ”ฐ What Is DynamoDB?โ€‹

DynamoDB is a fully managed, serverless, key-value and document NoSQL database designed for single-digit millisecond performance at any scale.

Analogy: Think of DynamoDB like a massive filing cabinet. Each drawer is a partition (identified by a partition key), and within each drawer, files are sorted by sort key. You can instantly find any file if you know which drawer (partition key) and which file label (sort key) to look for.

DynamoDB vs Relational Databasesโ€‹

FeatureDynamoDB (NoSQL)RDS/Aurora (SQL)
SchemaFlexible (each item can have different attributes)Fixed schema
ScalingHorizontal (automatic partitioning)Vertical (bigger instance)
JoinsโŒ Not supportedโœ… Native
Transactionsโœ… Limited (100 items, 4MB)โœ… Full ACID
Query flexibilityBy primary key + indexes onlyAny SQL query
PerformanceConsistent single-digit ms at any scaleVaries with load
Cost modelPay per request or capacityPay per instance hour

Core Conceptsโ€‹

Primary Key Typesโ€‹

TypeDescriptionWhen to Use
Partition Key (Hash Key)Single attribute, must be unique per itemSimple lookups by ID
Partition Key + Sort KeyComposite โ€” PK groups items, SK orders them within groupHierarchical data (userId + timestamp)

Partition Key Design (Critical!)โ€‹

โœ… Good partition keys (high cardinality, uniform distribution):
userId, orderId, sessionId, deviceId

โŒ Bad partition keys (low cardinality, hot partitions):
status ("active"/"inactive"), country, date

Hot partition example:
PK = "status" โ†’ 90% of items have status="active"
โ†’ One partition handles 90% of all traffic โ†’ THROTTLING!
Key Design Golden Rule

Choose a partition key that has many distinct values and uniform request distribution. If you must use a low-cardinality key, add a random suffix (write sharding) to spread load.

Item Size Limitโ€‹

  • Max item size: 400 KB (including attribute names)
  • For larger objects, store data in S3 and keep a reference in DynamoDB

Read/Write Capacity Modesโ€‹

ModeDescriptionBest ForSwitching
ProvisionedSet RCUs/WCUs manually (or auto-scale)Predictable, steady workloadsOnce per 24 hours
On-DemandAuto-scales, pay per requestUnpredictable, spiky workloadsOnce per 24 hours

Capacity Unit Calculationsโ€‹

READ CAPACITY UNITS (RCU):
1 RCU = 1 strongly consistent read of item โ‰ค 4 KB/sec
= 2 eventually consistent reads of items โ‰ค 4 KB/sec
= ยฝ transactional read of item โ‰ค 4 KB/sec

WRITE CAPACITY UNITS (WCU):
1 WCU = 1 write of item โ‰ค 1 KB/sec
= ยฝ transactional write of item โ‰ค 1 KB/sec

RCU Calculation Examplesโ€‹

Example 1: Read 10 items, 6 KB each, strongly consistent

Each item: ceil(6 KB / 4 KB) = 2 RCUs per item
Total: 10 ร— 2 = 20 RCUs

Example 2: Read 10 items, 6 KB each, eventually consistent

Each item: ceil(6 KB / 4 KB) = 2 RCUs strongly โ†’ 2/2 = 1 RCU eventually
Total: 10 ร— 1 = 10 RCUs

Example 3: Read 5 items, 17 KB each, transactional

Each item: ceil(17 KB / 4 KB) = 5 RCUs strongly โ†’ 5 ร— 2 = 10 RCUs transactional
Total: 5 ร— 10 = 50 RCUs

WCU Calculation Examplesโ€‹

Example 1: Write 6 items, 2.5 KB each per second

Each item: ceil(2.5 KB / 1 KB) = 3 WCUs per item
Total: 6 ร— 3 = 18 WCUs

Auto Scaling (Provisioned Mode)โ€‹

# CloudFormation auto scaling
TableWriteCapacityScaling:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MinCapacity: 5
MaxCapacity: 100
ResourceId: !Sub "table/${MyTable}"
ScalableDimension: dynamodb:table:WriteCapacityUnits
ServiceNamespace: dynamodb

Read Consistencyโ€‹

ModeLatencyFreshnessCost
Eventually ConsistentLowMay return stale data (~1s replication)1ร— (default)
Strongly ConsistentHigherAlways latest data2ร— RCUs
TransactionalHighestACID across multiple items4ร— RCUs
Exam Trap

GetItem and Query default to eventually consistent. You must explicitly set ConsistentRead: true for strong consistency. Scan also defaults to eventually consistent.


Secondary Indexesโ€‹

Local Secondary Index (LSI)โ€‹

PropertyValue
KeySame partition key, different sort key
CreationAt table creation time only
CapacityShares the table's RCU/WCU
ConsistencySupports strong and eventual consistency
Max per table5
ProjectionsKEYS_ONLY, INCLUDE, ALL

Global Secondary Index (GSI)โ€‹

PropertyValue
KeyDifferent partition key and/or sort key
CreationAny time (add/delete)
CapacityHas its own RCU/WCU (separate from table)
ConsistencyEventually consistent only
Max per table20
ProjectionsKEYS_ONLY, INCLUDE, ALL
Table: PK = userId, SK = orderId
LSI: PK = userId, SK = orderDate (same PK, different SK)
GSI: PK = status, SK = orderDate (completely different PK)
Decision Tree
  • Need to query on a non-key attribute? โ†’ GSI
  • Need a different sort key for the same partition? โ†’ LSI
  • Need strong consistency on the index? โ†’ LSI (GSI is always eventually consistent)
  • Table already exists and you need a new index? โ†’ GSI (LSI must be defined at creation)

GSI Throttling (Important!)โ€‹

If a GSI's WCU is throttled, the base table is also throttled โ€” even if the base table has enough capacity. Always provision GSI WCUs to match your write patterns.


DynamoDB Streamsโ€‹

  • Captures item-level changes (INSERT, MODIFY, REMOVE) in near real-time
  • Records available for 24 hours
  • Can trigger Lambda (Event Source Mapping)
  • Records are ordered within a shard (same partition key)

Stream View Typesโ€‹

View TypeContentUse Case
KEYS_ONLYOnly key attributesLightweight trigger (lookup full item if needed)
NEW_IMAGEComplete new itemReplicate to another table/service
OLD_IMAGEComplete old itemAudit what was there before
NEW_AND_OLD_IMAGESBoth before and afterDiff changes, full audit trail

Stream Use Casesโ€‹

  • Cross-region replication โ†’ Global Tables (built on Streams)
  • Trigger Lambda on data change โ†’ real-time processing
  • Audit trail โ†’ record all changes
  • Materialized views โ†’ aggregate data into summary tables
  • Search indexing โ†’ sync to OpenSearch/Elasticsearch

DAX (DynamoDB Accelerator)โ€‹

PropertyValue
TypeIn-memory write-through cache
LatencyMicroseconds (vs milliseconds for DynamoDB)
DeploymentCluster of nodes in your VPC
APIDrop-in replacement for DynamoDB client
ConsistencyEventually consistent only
// Switch from DynamoDB client to DAX client โ€” no code change!
DynamoDbClient daxClient = DynamoDbClient.builder()
.endpointOverride(URI.create("dax://my-cluster.abc123.dax-clusters.us-east-1.amazonaws.com:8111"))
.build();
DAX vs ElastiCache โ€” Exam Classic!
FeatureDAXElastiCache
Use withDynamoDB onlyAny database/application
IntegrationDrop-in (API compatible)Requires application code changes
CachingIndividual items + query resultsAny data you store (key-value)
ManagementAWS fully managedYou manage cluster/replication
Use caseDynamoDB read accelerationSession store, leaderboards, general cache

Transactionsโ€‹

// All-or-nothing: debit one account, credit another
dynamoDbClient.transactWriteItems(TransactWriteItemsRequest.builder()
.transactItems(
TransactWriteItem.builder()
.update(Update.builder()
.tableName("Accounts")
.key(Map.of("accountId", AttributeValue.fromS("ACC-001")))
.updateExpression("SET balance = balance - :amount")
.conditionExpression("balance >= :amount")
.expressionAttributeValues(Map.of(":amount", AttributeValue.fromN("100")))
.build())
.build(),
TransactWriteItem.builder()
.update(Update.builder()
.tableName("Accounts")
.key(Map.of("accountId", AttributeValue.fromS("ACC-002")))
.updateExpression("SET balance = balance + :amount")
.expressionAttributeValues(Map.of(":amount", AttributeValue.fromN("100")))
.build())
.build()
).build());
  • Up to 100 items or 4 MB per transaction
  • Consumes 2ร— RCUs/WCUs of non-transactional operations
  • Cannot target items in different AWS regions (use Global Tables for multi-region)

Key API Operationsโ€‹

OperationDescriptionRCU/WCU
PutItemCreate or replace entire itemWCU based on item size
GetItemRead single item by primary keyRCU based on item size
UpdateItemUpdate specific attributes (partial)WCU based on item size
DeleteItemDelete item by primary keyWCU based on item size
QueryItems with same PK, filter by SKRCU based on all items read
ScanRead all items in tableRCU for entire table
BatchGetItemUp to 100 items, multiple tablesSum of individual RCUs
BatchWriteItemUp to 25 Put/Delete operationsSum of individual WCUs
TransactGetItemsAtomic reads (up to 100 items)2ร— RCUs
TransactWriteItemsAtomic writes (up to 100 items)2ร— WCUs
Scan Is Expensive!

Scan reads the entire table โ€” filters are applied after reading, so they don't reduce RCU consumption. Always prefer Query or design GSIs to avoid scans. Use parallel scan with Segment and TotalSegments if scan is unavoidable.


TTL (Time to Live)โ€‹

{
"userId": "user-123",
"sessionData": "...",
"expiresAt": 1734567890
}
PropertyDetails
CostFree โ€” no WCU consumed for TTL deletions
Deletion timingWithin 48 hours of expiry (background process)
FormatUnix epoch in seconds (not milliseconds!)
Expired itemsStill returned in queries until deleted โ€” filter them!
StreamsTTL deletions appear in DynamoDB Streams (event type = REMOVE)

Conditional Writes & Expressionsโ€‹

// Optimistic locking โ€” only update if version matches
dynamoDbClient.updateItem(UpdateItemRequest.builder()
.tableName("Products")
.key(Map.of("productId", AttributeValue.fromS("P-001")))
.updateExpression("SET price = :newPrice, #v = #v + :inc")
.conditionExpression("#v = :expectedVersion")
.expressionAttributeNames(Map.of("#v", "version"))
.expressionAttributeValues(Map.of(
":newPrice", AttributeValue.fromN("99.99"),
":inc", AttributeValue.fromN("1"),
":expectedVersion", AttributeValue.fromN("5")))
.build());
// Throws ConditionalCheckFailedException if version doesn't match

Expression Typesโ€‹

ExpressionPurposeExample
ConditionExpressionConditional writesattribute_exists(email)
UpdateExpressionModify attributesSET #s = :val, ADD quantity :inc
ProjectionExpressionSelect attributes to returnorderId, #s, createdAt
FilterExpressionFilter Query/Scan results (post-read)status = :active
KeyConditionExpressionQuery key conditionsuserId = :uid AND orderDate > :date

๐Ÿ† Best Practicesโ€‹

Key Designโ€‹

  1. High-cardinality partition keys โ€” userId, orderId, not status or country
  2. Composite keys for hierarchical data โ€” PK=USER#123, SK=ORDER#2024-01-15
  3. Write sharding for hot keys โ€” append random suffix to spread load

Performanceโ€‹

  1. Use DAX for read-heavy workloads needing microsecond latency
  2. ProjectionExpression โ€” fetch only attributes you need
  3. Avoid Scans โ€” design GSIs for all access patterns
  4. BatchGetItem/BatchWriteItem โ€” reduce API calls (but handle UnprocessedItems!)

Costโ€‹

  1. On-Demand mode for unpredictable workloads โ€” no over-provisioning
  2. Use TTL for auto-expiring data โ€” free deletions
  3. Choose GSI projections wisely โ€” KEYS_ONLY or INCLUDE saves storage costs

๐ŸŽฏ DVA-C02 Exam Tipsโ€‹

DynamoDB Exam Cheat Sheet
  1. RCU/WCU calculations appear on almost every exam โ€” practice these
  2. GSI throttling affects base table โ€” always provision GSI capacity adequately
  3. LSI = table creation only; GSI = any time
  4. DAX = microsecond reads for DynamoDB; ElastiCache = general purpose
  5. Transactions = 2ร— cost (2ร— RCU for reads, 2ร— WCU for writes)
  6. TTL format: Unix epoch in seconds. Deletions happen within 48 hours
  7. Scan costs RCUs for entire table โ€” FilterExpression doesn't reduce cost
  8. Query always requires partition key in KeyConditionExpression
  9. Conditional writes prevent race conditions โ€” use ConditionExpression
  10. BatchWriteItem max 25 items; BatchGetItem max 100 items

๐Ÿงช Practice Questionsโ€‹

Q1. Find all orders for customer "C-100" sorted by date. Table: PK=customerId, SK=orderId. Best approach?

A) Scan with filter
B) Query on PK=C-100, sort by date
C) Create GSI: PK=customerId, SK=orderDate, then Query
D) GetItem for each orderId

โœ… Answer & Explanation

C โ€” Current SK is orderId, not date. A GSI with SK=orderDate enables sorted date queries. Alternatively, an LSI could work if you define it at table creation.


Q2. Read 10 items of 10 KB each, eventually consistent. How many RCUs?

A) 10
B) 15
C) 15
D) 30

โœ… Answer & Explanation

C โ€” Each 10KB item: ceil(10/4) = 3 RCU (strongly). Eventually consistent = 3/2 = 1.5 โ†’ round up to 2? No โ€” for eventually consistent, you halve the strongly consistent result: 3 RCU รท 2 = 1.5 RCU per item. Total = 10 ร— 1.5 = 15 RCUs.


Q3. Which Stream view captures both old and new item state?

A) KEYS_ONLY
B) NEW_IMAGE
C) OLD_IMAGE
D) NEW_AND_OLD_IMAGES

โœ… Answer & Explanation

D โ€” NEW_AND_OLD_IMAGES captures both states, ideal for auditing changes and computing diffs.


Q4. Sub-millisecond read latency on DynamoDB. What to use?

A) ElastiCache Redis
B) Read Replicas
C) DAX
D) Strongly Consistent Reads

โœ… Answer & Explanation

C โ€” DAX provides microsecond latency for DynamoDB reads. ElastiCache works but requires code changes. DynamoDB doesn't have read replicas. Strong consistency is slower.


Q5. A GSI is throttled. What else is affected?

A) Only the GSI queries are throttled
B) Base table writes are also throttled
C) Other GSIs on the same table are throttled
D) Nothing else is affected

โœ… Answer & Explanation

B โ€” When a GSI is throttled, DynamoDB throttles writes to the base table to prevent the GSI from falling behind. Always ensure GSI WCUs match your write patterns.


๐Ÿ”— Resourcesโ€‹