LearnThatStack Ace your next interview

DynamoDB

boto3 calls and DynamoDB expressions Checked Oct 2026 53 commands

The model

  • DynamoDB hashes an item's partition key to choose the partition that stores it. A sort key orders the items that share a partition key.
  • Fast reads name a key. GetItem takes the whole key, and Query takes one partition key. A Scan reads every item.
  • Reads are eventually consistent unless you set ConsistentRead=True. A global secondary index (GSI) updates asynchronously, so its reads are always eventually consistent.
  • One partition serves at most 3,000 read units and 1,000 write units a second. A busy key throttles even when the table has spare capacity.
  • There are no joins. You design the keys from the access patterns, so each query is one GetItem or Query.
Appboto3 callLeader copypicked by hash(PK)Other copiestwo more AZsGSIits own partitionsWRITEPutItemreplicateOKupdate asyncREADConsistentRead latesteventual may be staleGSI read eventual only
Each partition has three copies in three Availability Zones. Writes and strong reads go to the leader copy. A default read or a GSI read may be stale.

What DynamoDB is

a serverless key-value store, and when to pick it

What DynamoDB is

Say what DynamoDB isA serverless NoSQL database from AWS for key-value items and documents.
Say how fast and available it isSingle-digit milliseconds at any scale. The SLA promises 99.99% availability, or 99.999% for global tables.
Choose it over a relational databaseFor known access patterns at any scale. Ad hoc queries and joins suit SQL.

TrapDon't say DynamoDB lacks strong reads or transactions. It has both, when you ask for them.

DynamoDB Local

Run DynamoDB on your own machinedocker run -p 8000:8000 amazon/dynamodb-localOr run the free download with Java.
Point the SDK at itendpoint_url="http://localhost:8000"The SDK still needs keys and a Region, but any values work.

TrapDynamoDB Local ignores provisioned throughput and has no IAM. Test capacity, throttling and permissions on the real service.

Keys, items & limits

how DynamoDB finds an item, and what it can hold

Partition key and sort key

Identify each item by one attributeA simple primary key, the partition key alone. Each value must be unique.
Keep many items under one partition keyUse a composite primary key, the partition key plus a sort key. Each pair must be unique.
Mark the keys when you create a tableKeyType="HASH"KeyType="RANGE"HASH is the partition key, and RANGE is the sort key.

TrapYou can't change a table's primary key later. A new key means a new table and a copy of the data.

Data types

Name the scalar typesString S, Number N, Binary B, Boolean BOOL and Null NULL.
Name the document and set typesList L and Map M nest values. Sets are SS, NS and BS.
Write a value in the low-level API{"id": {"S": "123"}}Each value names its type.

TrapKey attributes must be String, Number or Binary. There's no date type, so store an ISO string or an epoch number.

Limits to know

Name the item size limit400 KB, counting attribute names as well as values.
Say how many indexes a table can have20 GSIs by default, and 5 local secondary indexes (LSIs).
Name the batch limitsBatchGetItem reads 100 items or 16 MB. BatchWriteItem writes 25 items.

TrapAttribute names count toward the 400 KB. Long names on every item cost storage and capacity.

Read & write items

get, put, update, delete, query and scan

Get, put, update, delete

Read one item by its full keyget_item(Key=key)
Write a whole itemput_item(Item=item)It creates the item, or replaces one with the same key.
Change some attributes, keep the restSET price = :pAn UpdateItem expression. It creates a missing item too, so both calls upsert.
Add 1 to a counter without reading itSET #v = if_not_exists(#v, :zero) + :oneAn atomic counter. A retried call can count twice.
Delete one itemdelete_item(Key=key)
Put or delete items in batchesbatch_write_item(RequestItems=req)Retry the UnprocessedItems it returns.

Tipviews is a reserved word. Write #v in the expression and map it with ExpressionAttributeNames.

TrapPutItem drops every attribute you leave out. Use UpdateItem to change only some.

Query or Scan

Read the items under one partition keyPK = :pkA Query key condition. Items come back in sort key order.
Narrow a Query by sort key prefixbegins_with(SK, :prefix)
Return the newest items firstScanIndexForward=False
Read every item in a tablescan()Its cost grows with the table, not with the result.
Drop items from the resultsFilterExpression="age > :a"DynamoDB applies it after the read.

TipProjectionExpression returns fewer attributes, but you still pay to read the whole item.

TrapA filter doesn't lower the cost. Items it drops still use read capacity.

Paginate results

Stop each call after 25 itemsLimit=25It counts items read, not items returned.
Fetch the next pageExclusiveStartKey=last_keySet it to the last response's LastEvaluatedKey. A response holds at most 1 MB.
Jump straight to page 5You can't. Pages follow keys, so you only move forward from a saved key.

TrapWith a filter, a page can be empty while more items remain. Stop only when LastEvaluatedKey is missing.

PartiQL

Query with SQL syntaxSELECT * FROM Orders WHERE PK = 'U#1'It reads fast because WHERE names the partition key, with = or IN.
Run up to 25 statements in one callBatchExecuteStatement. They must be all reads or all writes.

TrapA SELECT without the partition key scans the whole table. An IAM policy can deny those scans.

Consistency & transactions

fresh reads, safe concurrent writes, all or nothing

Eventual or strong reads

Say what a default read returnsAn eventually consistent read. It may miss a write made a moment ago.
Read the latest writeConsistentRead=TrueIt costs twice the read units. It works on tables and LSIs only.
Show users their own write at onceReturnValues="ALL_NEW"Use the item the update returns, instead of reading it back.

TrapA GSI query is always eventually consistent, and ConsistentRead=True on it returns an error. Retry a missed new item with backoff.

Conditional writes

Write an item only if its key is newattribute_not_exists(PK)
Update only an item that already existsattribute_exists(PK)
Stop two clients overwriting each otherversion = :expectedOptimistic locking. Set version one higher in the same write.
Handle a failed conditionConditionalCheckFailedExceptionNothing was written. Read the item again and retry.

TipPass a condition as ConditionExpression. DynamoDB checks it and writes in one atomic step.

TrapA failed condition still uses write capacity.

Transactions

Write up to 100 items all or nothingtransact_write_items(TransactItems=items)Across tables in one Region. Condition checks count toward the 100.
Read up to 100 items as one snapshottransact_get_items(TransactItems=items)
Name the other transaction limits4 MB in total, and the same item can't appear twice.
Retry a transaction without applying it twiceClientRequestToken=tokenThe token makes it idempotent for 10 minutes.

TipFor bulk loads, use BatchWriteItem. It costs less, but some items can fail.

TrapDon't say 25 items. That limit rose to 100 in September 2022.

Secondary indexes

query by attributes outside the primary key

GSI or LSI

A global secondary index (GSI).Query by a different partition key
A local secondary index (LSI).Sort a partition key's items another way, with strong reads
yesno

TipPick a GSI for most new access patterns. An LSI fits only when you need strong reads in another sort order.

Global secondary indexes

Add a GSI to an existing tableGlobalSecondaryIndexUpdatesPass a Create action in UpdateTable.
Query a GSIIndexName="ByStatus"
Choose a GSI's projection, the attributes it copiesKEYS_ONLYINCLUDEALLA GSI query can't fetch the others.

TrapA GSI short of write capacity throttles writes to its base table too.

Sparse indexes

Index only items that have an attributeA sparse index. DynamoDB leaves out items without the index key.
List only premium usersSet premium_expires only on premium items, and index it.
Take an item out of the indexREMOVE premium_expiresDynamoDB drops it from the index.

TipA sparse index holds fewer items, so it costs less to store and to query.

Data modeling

shape keys around the queries you'll run

Design from the access patterns

Start a table designList every query first. Then shape keys so each is one GetItem or Query.
Replace a joinDenormalize. Copy the data you would join into the item you read.
Decide when to add a GSIOnly for a pattern the table's keys can't serve. Each GSI adds write cost.

TrapA new access pattern later can mean a new GSI or a data migration. This design trades flexibility for speed.

Single-table design

PK SK attributesUSER#42 PROFILE name, emailUSER#42 ORDER#2026-09-14 total, statusUSER#42 ORDER#2026-10-02 total, statusORDER#7 ITEM#ABC product, qty
Get a user and all their ordersOne Query on PK = USER#42.
Serve several access patterns from one GSIGSI overloading. Each item type puts its own kind of value in the same index key.
Name its pros and consOne request gets several entity types. But it's hard to learn and needs every pattern up front.

TrapDon't force it. If the app rarely reads entity types together, one table per entity is simpler.

One-to-many and many-to-many

Model a user's ordersPut them under the user's partition key, with ORDER# plus the date as sort key.
Store a few children always read with the parentUse a list or map on the parent. Keep the item under 400 KB.
Model students and coursesAn adjacency list. Each enrollment is an item, keyed by student and course.
List the students in one courseAdd a GSI with PK and SK swapped, and query it on the course.

TipFor queries that walk many hops of a graph, a graph database such as Amazon Neptune fits better.

TrapCopying a course title into each enrollment saves a read. But a title change must then update every copy.

Time-series data

Read one device's readings for a time rangeSK BETWEEN :start AND :endDevice ID as partition key, an ISO 8601 time as sort key.
Store a high-volume event streamOne table per day or month. Create the next one before the current one ends.
Drop a whole period at oncedelete_table(TableName=name)

TrapNever make a date rounded to the day or hour the whole partition key. That period's writes all hit one partition.

Items over 400 KB

Store a file far over 400 KBPut it in S3. Keep its bucket, key and size in the item.
Split a large documentStore chunks under one partition key, with sort keys chunk#0, chunk#1. Add a metadata item.
Fit long text under the limitGzip it into a Binary attribute. You can't filter or query on it then.

TrapS3 and DynamoDB share no transaction. Clean up S3 objects that a failed write leaves behind.

Capacity & throttling

what reads and writes cost, and why they throttle

Capacity units

Say what one read capacity unit (RCU) buysOne strongly consistent read a second of up to 4 KB, or two eventually consistent ones.
Say what one write capacity unit (WCU) buysOne write a second of up to 1 KB.
Price an 8 KB read and a 3 KB writeThe read takes 2 RCUs, or 1 if eventually consistent. The write takes 3 WCUs.

TipDynamoDB rounds item size up to the next 4 KB for reads and 1 KB for writes. Transactions use twice the units.

Capacity modes and cost

Pay per request, with no capacity planningBillingMode="PAY_PER_REQUEST"On-demand. AWS now calls it the default for most workloads.
Pay for set reads and writes per secondBillingMode="PROVISIONED"Provisioned, billed by the hour. Add auto scaling, and reserved capacity for steady loads.
Decide when provisioned is cheaperAbove about 29% average use, since AWS halved on-demand prices in November 2024.
Cut storage cost for a rarely read tableTableClass="STANDARD_INFREQUENT_ACCESS"Standard-IA. Storage costs less, but reads and writes cost more.
Prepare a table for a known spikeRaise its warm throughput to the expected peak. AWS added it in November 2024.

TrapOn-demand still throttles. Traffic that more than doubles its previous peak within 30 minutes can be throttled.

Throttling

Recognize a throttled requestProvisionedThroughputExceededException
Handle throttling in codeRetry with exponential backoff. The AWS SDKs already do.
Say how DynamoDB absorbs a spikeBurst capacity keeps up to 5 minutes of unused capacity. Adaptive capacity gives a busy partition more.

TrapLarge items throttle sooner, because each request uses more capacity units.

Partition keys and hot keys

Pick a partition keyMany distinct values, called high cardinality, and even traffic per value, like user_id.
Spot a bad partition keyFew values, like status, or one value far busier than the rest.
Spread one busy key over several partitionsWrite sharding. Add a suffix from 1 to N, then query every suffix to read.
Keep single-item reads direct after shardingTake the suffix from a hash of a field you know, like the order ID.

TipFor a key that gets heavy reads, cache it with DAX or in the app.

TrapDynamoDB can't split one partition key's items across partitions when the table has an LSI.

TTL, Streams & DAX

expire items, react to changes, cache reads

Time to Live (TTL)

Turn on TTL for a tableCall UpdateTimeToLive with the name of the expiry attribute.
Make an item expire in one hour"expires_at": int(time.time()) + 3600A Number, in Unix epoch seconds. TTL ignores other types.
Hide expired items DynamoDB hasn't deleted yetexpires_at > :nowAdd it as a filter expression.
Say what TTL deletes costNothing, since they use no write capacity. Replicated deletes in a global table are charged.

TipTTL deletes reach the table's stream as service deletions, so a consumer can archive them.

TrapDynamoDB deletes an expired item within a few days, not at once. Reads still return it until then.

DynamoDB Streams

Turn on a stream with before and after imagesStreamViewType="NEW_AND_OLD_IMAGES"The other view types are KEYS_ONLY, NEW_IMAGE and OLD_IMAGE.
Run code on every changeMake a Lambda function the stream's trigger.
Say what order records arrive inEach change appears once, in order for each item. There's no order across items.

TipUse streams for cache invalidation, audit trails, analytics and copies to another store.

TrapA stream keeps records for 24 hours. A consumer that falls further behind loses them.

DynamoDB Accelerator (DAX)

Say what DAX isA managed in-memory write-through cache. Cached reads take microseconds.
Decide when to use DAXFor read-heavy work that rereads the same items, like leaderboards.
Decide when to skip itWhen reads must be strongly consistent, writes dominate, or reads rarely repeat.

TipThe DAX client has DynamoDB's API, so switching takes few code changes.

TrapWrites that bypass DAX leave its cache stale until each entry's TTL runs out. Writes never update its query cache.

Global tables & backups

survive a Region failure or a bad write

Global tables

MRECwritecopyOKMRSCwrite + copyOKtime

MREC confirms a write before other Regions have it, so a Region failure can lose a few seconds. MRSC waits for another Region, so writes are slower.

Copy writes to other Regions in the backgroundMulti-Region eventual consistency (MREC), the default. Replicas usually catch up within a second.
Read the latest write in any Region (2025+)Multi-Region strong consistency (MRSC). It needs exactly three Regions and has no transactions or TTL.

TipEach replica uses write capacity for every write. Read capacity can differ per replica.

TrapUnder MREC, two Regions can write the same item, and the last writer wins. Give each item one home Region.

Backups and restore

Restore to any point in the last 35 daysPointInTimeRecoveryEnabled=TruePoint-in-time recovery (PITR). You choose a window of 1 to 35 days.
Keep a full copy until you delete itcreate_backup(TableName=t, BackupName=name)An on-demand backup.
Copy backups to another Region or accountUse AWS Backup. It adds schedules too.
Restore a tableDynamoDB restores into a new table, so switch the app over.

TipBackups use no table capacity, so they don't slow the table.

TrapA restore brings back the data and indexes, but not TTL, streams or auto scaling. Set those again.

High availability and recovery

Survive an Availability Zone failureNothing to do. DynamoDB already copies every table to three zones.
Survive a Region failureGlobal tables. MREC can lose a few seconds of writes, and MRSC none.
Stop anyone deleting a table by mistakeDeletionProtectionEnabled=True

TrapReplication isn't a backup. A bad delete reaches every Region, so keep point-in-time recovery on.

Security & monitoring

control access, and spot throttling early

Secure a table

Let users reach only their own itemsdynamodb:LeadingKeysFine-grained access control. This IAM condition matches the partition key to the user's ID.
Share a table with another accountA resource-based policy on the table. It covers the table's indexes too.
Keep traffic off the public internetA VPC endpoint.
Encrypt data at restAlways on. Keep the default AWS owned key, or use a key in AWS KMS.
Hide sensitive attributes from DynamoDB itselfClient-side encryption, with the AWS Database Encryption SDK.

TipIAM policies decide who calls which actions on which tables. CloudTrail logs every call.

Monitor and alarm

Watch capacity useConsumedReadCapacityUnitsConsumedWriteCapacityUnits
Alarm on throttlingReadThrottleEventsWriteThrottleEvents
Spot a hot partitionWriteKeyRangeThroughputThrottleEventsOr turn on CloudWatch Contributor Insights.
Measure latency inside DynamoDBSuccessfulRequestLatency

TrapThrottledRequests and SystemErrors carry an operation dimension. An alarm on the table name alone never gets data.

Found this useful? Pass it on.
Pro · $10/mo

This sheet stays free. With Pro, you can:

  • Every answer on this sheet. Read the full answer to all 38 questions it links, not just the 5 free ones.
  • My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
  • PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
  • The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.