DynamoDB has somewhat of a learning curve, I get it.
I’ve been there. You probably are there now.
You chose DynamoDB because of it’s promise to deliver a few millisecond query latencies.
But you just can’t get it to deliver on that promise for your queries.
You have no idea why.
Usually, there are two principle reasons as to why this happens.
In this article, I’ll take you through those reasons I’ve seen occur most frequently and offer guides on how to fix these issues.
1. You’re using the Scan method
This is perhaps the most common reason for almost all beginners starting with DynamoDB.
As you start designing your tables with DynamoDB, you’ll run into workloads that will seemingly necessitate Scans. Using Query is quite restrictive from a beginner’s perspective and so you’ll instinctively reach for the Scan() method.
Here’s the reason:
The Scan method allows you to perform any kind of filtering you need, including “contains” operations which will usually satisfy any type of filtering you may need.
The Query method on the other hand supports a limited set of operations. At its core, the Query method only supports three operations:
Equality operators (<, >, <=, >=, ==, to compare strings and numbers)
begins_with() operator (to match a string prefix)
BETWEEN operator (to match a range between two string/numbers values)
With Query(), there is no “all powerful” contains filter to filter for any attribute value.
Now say, for example, you need to query for order items and filter by a date range. E.g. “Get all of a customer’s orders for the month of June”.
Assume we have the following data in our table:
With the Scan operator you can get all items and use the contains clause, saying “orderDate contains(‘2025–06’)” (highlighted in blue).
You’d get all orders placed in June.
It’s easy and very flexible.
However the issue with this “Query” is that it scans across every item in your table until it reaches the end and returns all matches, hence why it is called a Scan.
If you’ve got a million records stored in your table, this will be the reason the query will be very slow and very expensive.
How to remedy this
There is an alternative to this. One that will make good on DynamoDB’s “single-digit millisecond query latency” at any scale.
It’s about properly modeling your data to satisfy your access pattern — the dataset you want to fetch — and molding it to fit either of the three Query() methods we saw above (or eight if you count the equality operators separately).
I’ll continue to expand on this in the section below. The takeaway from this section is: stop using Scans.
2. You’re not designing your sort key properly
This is usually the most common issue I see from folks who already know to avoid Scans.
Understanding the first issue, won’t help you much if you don’t understand how to design your sort keys properly.
In our example above, getting orders by a given month we saw how easy (and wrong) it was to use the Scan() method.
Most beginners use a simple sort key.
In our example that’s: “sk: o#201”.
In a user’s table that maybe something like “sk: john@gmail.com“.
These are simple, single values.
To design more powerful sort keys we need to add multiple values inside our sort keys and structure them in such a way to enable flexible filtering.
How to remedy this
Let’s see the solution to fetching this same exact dataset, but this time using one of the three Query methods, while benefitting from single-digit millisecond latency queries.
The only change we made here is the sort key (highlighted in blue) now concatenates the date with the orderID, making our new orderID (sk) value consist of the format “o#<date>#<orderID>”.
With this “optimized” data model, we can then execute the BETWEEN operator from the Query methods:
TableName: "orders",
KeyConditionExpression: "customerID = c#101 AND orderID BETWEEN o#2025-06-01# AND o#2025-06-31#"
The KeyConditionExpression says: “get items whose customerID (pk) value is equal to ‘c#101’ and whose orderID (sk) value lies alphanumerically between o#2025–06–01# AND o#2025–06–31#”.
This is going to essentially retrieve all order items made in the month of June.
👋 My name is Uriel Bitton and I’m committed to helping you master Serverless, Cloud Computing, and AWS.
🚀 If you want to learn how to build serverless, scalable, and resilient applications, you can also follow me on Linkedin for valuable daily posts.
Thanks for reading and see you in the next one!