DynamoDB Cheat Sheet
AWS CLI and boto3 commands plus core DynamoDB concepts like partition keys, indexes, and expressions for NoSQL data modeling.
AWS CLI Basics
Creating a table and reading/writing items.
aws dynamodb create-table \ --table-name Users \ --attribute-definitions AttributeName=UserId,AttributeType=S \ --key-schema AttributeName=UserId,KeyType=HASH \ --billing-mode PAY_PER_REQUESTaws dynamodb put-item --table-name Users \ --item '{"UserId": {"S": "u1"}, "Email": {"S": "[email protected]"}}'aws dynamodb get-item --table-name Users \ --key '{"UserId": {"S": "u1"}}'
boto3 (Python SDK)
Using the high-level Table resource.
import boto3dynamodb = boto3.resource('dynamodb')table = dynamodb.Table('Users')table.put_item(Item={'UserId': 'u1', 'Email': '[email protected]'})resp = table.get_item(Key={'UserId': 'u1'})item = resp.get('Item')table.update_item( Key={'UserId': 'u1'}, UpdateExpression='SET Email = :e', ExpressionAttributeValues={':e': '[email protected]'})
Core Concepts
Key building blocks of the DynamoDB model.
- Partition key- hash key that determines how items are distributed across partitions
- Sort key- optional range key that orders items sharing a partition key
- GSI- Global Secondary Index; an alternate partition/sort key for new query patterns
- LSI- Local Secondary Index; alternate sort key, same partition key, set at table creation
- On-demand vs provisioned- pay-per-request billing versus fixed RCU/WCU capacity
- DynamoDB Streams- an ordered, time-limited feed of item-level table changes
Key Operations
The main ways to read and write data.
- GetItem- retrieves a single item by its exact primary key
- Query- retrieves items sharing a partition key, optionally filtered by sort key
- Scan- reads every item in a table; expensive, avoid for large tables
- ConditionExpression- writes only if a condition holds, enabling optimistic locking
- BatchWriteItem- writes or deletes up to 25 items in a single call
- TransactWriteItems- performs an atomic write across multiple items/tables
Single-Table Design with Overloaded Keys
Storing multiple entity types in one table using generic PK/SK names and a GSI for inverse lookups.
# One table, multiple entity types, disambiguated by key prefixes# PK SK Entity# USER#u1 METADATA user profile# USER#u1 ORDER#o1 order belonging to user# ORDER#o1 METADATA order record (for direct lookup)table.put_item(Item={ 'PK': 'USER#u1', 'SK': 'METADATA', 'Type': 'User', 'Email': '[email protected]'})table.put_item(Item={ 'PK': 'USER#u1', 'SK': 'ORDER#o1', 'Type': 'Order', 'GSI1PK': 'ORDER#o1', 'GSI1SK': 'METADATA'})# Query all of a user's orders in one requesttable.query( KeyConditionExpression='PK = :pk AND begins_with(SK, :sk)', ExpressionAttributeValues={':pk': 'USER#u1', ':sk': 'ORDER#'})# Query the order directly via the GSI without scanningtable.query( IndexName='GSI1', KeyConditionExpression='GSI1PK = :pk', ExpressionAttributeValues={':pk': 'ORDER#o1'})
DynamoDB Streams + Lambda Trigger
Reacting to item-level changes in near real time for fan-out, caching, or search indexing.
def lambda_handler(event, context): for record in event['Records']: event_name = record['eventName'] # INSERT | MODIFY | REMOVE if event_name == 'INSERT': new_image = record['dynamodb']['NewImage'] handle_created(new_image) elif event_name == 'MODIFY': old_image = record['dynamodb']['OldImage'] new_image = record['dynamodb']['NewImage'] handle_updated(old_image, new_image) elif event_name == 'REMOVE': handle_deleted(record['dynamodb']['Keys'])# Enable on the table with StreamViewType=NEW_AND_OLD_IMAGES to get both# aws dynamodb update-table --table-name Users --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
Optimistic Locking with TransactWriteItems
Enforcing atomic multi-item invariants and preventing lost updates with a version attribute.
import boto3from botocore.exceptions import ClientErrorclient = boto3.client('dynamodb')try: client.transact_write_items(TransactItems=[ { 'Update': { 'TableName': 'Accounts', 'Key': {'AccountId': {'S': 'a1'}}, 'UpdateExpression': 'SET Balance = Balance - :amt, Version = Version + :one', 'ConditionExpression': 'Version = :v AND Balance >= :amt', 'ExpressionAttributeValues': { ':amt': {'N': '50'}, ':v': {'N': '3'}, ':one': {'N': '1'} } } }, { 'Update': { 'TableName': 'Accounts', 'Key': {'AccountId': {'S': 'a2'}}, 'UpdateExpression': 'SET Balance = Balance + :amt', 'ExpressionAttributeValues': {':amt': {'N': '50'}} } } ])except ClientError as e: if e.response['Error']['Code'] == 'TransactionCanceledException': retry_with_fresh_version()
PartiQL for DynamoDB
Running SQL-compatible statements against DynamoDB for ad-hoc reads and writes.
-- SELECT still requires the partition key for an efficient (non-scan) querySELECT * FROM "Users" WHERE "UserId" = 'u1';INSERT INTO "Users" VALUE {'UserId': 'u2', 'Email': '[email protected]'};UPDATE "Users" SET "Email" = '[email protected]' WHERE "UserId" = 'u2';DELETE FROM "Users" WHERE "UserId" = 'u2';-- Batch execute via aws dynamodb execute-statement / batch-execute-statement CLI or ExecuteStatement API
Capacity & Performance Tuning
Levers for keeping a table fast and cost-efficient at scale.
- Hot partition- uneven access to one partition key throttles requests even under on-demand billing; fix with higher-cardinality keys or write sharding
- Adaptive capacity- DynamoDB automatically shifts throughput to hot partitions, but it's not instantaneous
- DAX- an in-memory, write-through cache cluster for microsecond read latency in front of DynamoDB
- Point-in-time recovery (PITR)- continuous backups letting you restore to any second in the last 35 days
- TTL attribute- a Unix-epoch number attribute that triggers automatic, free item deletion
- Eventually vs strongly consistent reads- GetItem/Query default to eventual consistency; set ConsistentRead=True for read-after-write guarantees
- Write sharding- appending a random or hashed suffix to a partition key to spread writes across more partitions
Design your table around access patterns first — DynamoDB has no JOINs, so single-table design up front is far cheaper than retrofitting new query patterns onto an existing key schema later.