Java (AWS SDK v2) での DynamoDB Scan
scanPaginator は LastEvaluatedKey を代わりに面倒みてくれます。そして次に多くの人が手を伸ばすつまみ — .limit(...) — は、同じスキャンを遅く、そしてわずかに高くします。そもそも Scan を完全に避けるべきときについては Query と Scan の比較を参照してください。
コード
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
import software.amazon.awssdk.services.dynamodb.model.ScanResponse;
public class ScanExample {
public static void main(String[] args) {
try (DynamoDbClient ddb = DynamoDbClient.builder()
.region(Region.US_EAST_1)
.build()) {
Map<String, String> names = new HashMap<>();
names.put("#filter0", "Year");
Map<String, AttributeValue> values = new HashMap<>();
values.put(":filterValue0", AttributeValue.builder().n("2010").build());
ScanRequest request = ScanRequest.builder()
.tableName("Music")
.filterExpression("#filter0 >= :filterValue0")
.expressionAttributeNames(names)
.expressionAttributeValues(values)
.build();
List<Map<String, AttributeValue>> items = new ArrayList<>();
for (ScanResponse page : ddb.scanPaginator(request)) {
items.addAll(page.items());
}
System.out.println("Matched " + items.size() + " items");
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
}
}
}.limit(25) は 3 リクエストを 25 に変え、しかも高くつく
フィクスチャにはそれぞれ約 3.9 KB の 600 曲が入っており、そのうち 8 曲が条件に合致します。書いたとおりに例を実行し、次に .limit(25) を付けて実行します。
| リクエストの形 | 往復回数 | 読み取りユニット | 返ったアイテム数 |
|---|---|---|---|
| 書いたとおり | 3 | 284.5 | 8 |
.limit(25) | 25 | 288.0 | 8 |
キャパシティはページ単位で、4 KB の境界に切り上げて課金されます。したがって 1 MB の読み取り 1 回を小さな 24 回に切り刻めば、その切り上げを 24 回払うことになります。25 回目のリクエストがもう 1 つの意外な点です。24 ページ目でテーブルは読み終わっていたのに LastEvaluatedKey が返り、ページネーターがもう一度尋ねて scannedCount=0 を得ました。DynamoDB は「もうデータはない」ことを、短いページを返すことではなく、そのキーを省くことで知らせます。そして Limit の境界では、まだ分かっていないのです。
.limit(...) は、ライブのテーブルをスロットリングさせたくないバックグラウンドジョブのための、キャパシティ平準化のつまみです。スキャンを安く、あるいは短くする手段ではありません。
エイリアスは様式ではない
#filter0 を外して Year を直接フィルターに使うと、SDK は awsErrorDetails() を通じてこう表面化させます。
DynamoDbException / ValidationException /
Invalid FilterExpression: Attribute name is a reserved keyword; reserved keyword: Year
/ http 400Year は 573 個の予約語のひとつです。この SDK のページネーターのエラーはどれもそうですが、これは scanPaginator(request) を呼んだときではなく for ループの最初の反復で届くので、try はループを包む必要があります。
2026-07-28 に、OpenJDK 26.0.1 上の software.amazon.awssdk:dynamodb 2.49.4 で DynamoDB Local(amazon/dynamodb-local)に対して測定しました。
解説
- 最初の 2 ページは 0 件を返します。このフィルターではページごとの結果が 0、0、8 件で、読み取りユニットは 128.5、128.5、27.5 です。
filterExpressionは読み取りの後に走るので、その 2 つの空のレスポンスにも満額かかります。そしてif (page.items().isEmpty()) breakはどれも、テーブルが空だと報告します。 ddb.scanPaginator(request).items()はページを 1 つのIterable<Map<String, AttributeValue>>に平坦化し、背後でページングするので、アイテムだけが欲しいときに入れ子のループを畳めます。SdkIterableなので.stream()も使え、ここでは.items().stream().count()が 8 を返します。ScanIterableは反復のたびにスキャンを走らせ直します。遅延評価であってキャッシュではありません。同じオブジェクトを 2 周すればリクエストも 2 回送られ、課金も 2 回です。例のように、一度Listに汲み出しましょう。- ビルダーでは数値は
Stringです。AttributeValue.builder().n("2010")はjava.lang.Stringを取ります。DynamoDB が数値を 10 進のテキストとして運ぶからです。intを渡してもコンパイルできません。 .segment(...)/.totalSegments(...)はテーブル全体のスキャンをワーカー間で分割し、それぞれが自分のページネーターを持ちます。これは実時間を割り算しますが、消費するキャパシティは同じです。
ビジュアルに行う
予約語チェッカーは、属性名を AWS の完全なリストに照らして走らせ、ExpressionAttributeNames のマップを返します。Year、Name、Size、Status がどれも押さえられていることを ValidationException 1 つずつ学ぶより速いです。
ScanRequest に組み込む前に実際のテーブルでフィルターを試すには、DynoTable をダウンロードして、結果をグリッドでページングしてください。
関連する例
- Go での DynamoDB Scan — AWS SDK for Go v2 での同じスキャン。
- Java での DynamoDB Query — 普段手を伸ばすべき、より安い読み取り。
- Query と Scan の比較 — (まれに)
Scanが正当化されるのはどんなときか。 - DynamoDB の Scan はなぜ遅くて高いのか? — コストモデルと、その回避方法。
- DynamoDB ProvisionedThroughputExceededException — テーブル全体のスキャンが、プロビジョニング済みテーブルのキャパシティに何をするか。
- DynamoDB ThrottlingException — もう一方のスロットリングと、指数バックオフでの対処。
参考資料
- Scan — Amazon DynamoDB API Reference
- Use Scan with an AWS SDK or CLI — Amazon DynamoDB Developer Guide
- DynamoDbClient — AWS SDK for Java 2.x API Reference
- ScanRequest — AWS SDK for Java 2.x API Reference
- Scanning tables — Amazon DynamoDB Developer Guide
- Reserved words in DynamoDB — Amazon DynamoDB Developer Guide