Go での DynamoDB Query(AWS SDK v2)

Query は 1 つのパーティションを読み、必要ならソートキーで絞り込みます(それが正しい選択となる場面は Query と Scan の比較が扱い、使える演算子はキー条件式が網羅しています)。AWS SDK for Go v2 が上乗せするのは QueryPaginator で、LastEvaluatedKey のループを for ループに変えてくれる一方、その過程で「止まる」機会をそっと奪います。

コード

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

func main() {
	ctx := context.TODO()
	cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1"))
	if err != nil {
		log.Fatalf("load config: %v", err)
	}
	client := dynamodb.NewFromConfig(cfg)

	paginator := dynamodb.NewQueryPaginator(client, &dynamodb.QueryInput{
		TableName: aws.String("Music"),
		KeyConditionExpression: aws.String(
			"#hashKey = :hashKeyValue AND begins_with(#rangeKey, :rangeKeyValue)"),
		ExpressionAttributeNames: map[string]string{
			"#hashKey":  "Artist",
			"#rangeKey": "SongTitle",
		},
		ExpressionAttributeValues: map[string]types.AttributeValue{
			":hashKeyValue":  &types.AttributeValueMemberS{Value: "Arturo Sandoval"},
			":rangeKeyValue": &types.AttributeValueMemberS{Value: "C"},
		},
	})

	var items []map[string]types.AttributeValue
	for paginator.HasMorePages() {
		page, err := paginator.NextPage(ctx)
		if err != nil {
			log.Fatalf("query: %v", err)
		}
		items = append(items, page.Items...)
	}
	fmt.Printf("Found %d items\n", len(items))
}

解説

Limit はページネータが読む量を制限しません。これがお金のかかる誤解です。上の入力に Limit: aws.Int32(5) を設定し、約 60 KB のアイテム 30 件からなるパーティションに対して実行した結果です。

page 1: Count=5 CU=37.0   page 5: Count=5 CU=37.0
page 2: Count=5 CU=37.0   page 6: Count=5 CU=37.0
page 3: Count=5 CU=37.0   page 7: Count=0 CU=0.0
page 4: Count=5 CU=37.0   total: 30 items, 222.0 units

30 件すべてが返ってきました。Limitページサイズ であり、ページネータの仕事はページが尽きるまで問い合わせ続けることなので、両者はきれいに打ち消し合います。AWS はこれを「the maximum number of items to evaluate (not necessarily the number of matching items)」と定義しています(2026-07-28 取得)。最初の 5 件だけが欲しいなら、1 ページ目の後に自分でループを break してください。

小さなページはむしろ少し高くつきます。同じパーティションを Limit なしで読むと 2 ページで 220.0 ユニット、Limit: 5 では 7 回の呼び出しで 222.0 ユニットでした。各ページは自分のバイト合計を次の 4 KB 境界まで切り上げるので、ページが増えれば丸めも増え、加えてラウンドトリップのレイテンシが 6 回分余計にかかります。「読む量を減らそう」と Limit を下げても、どちらも手に入りません。

このループは、データがある回数より必ず 1 回多く呼び出します。上の 7 ページ目はゼロ件を返しました。DynamoDB は Limit に達したときは後続の有無にかかわらず LastEvaluatedKey を返し、HasMorePages() はそれを信じます。つまり Limit 付きのページネータは無駄なリクエストで終わり、ページごとの副作用(プログレスバー、バッチのフラッシュ、ログ行)は空のページに対して 1 回発火します。len(page.Items) でガードしてください。

HasMorePages() は最初のリクエストの前から true ですfor ループにそもそも入れるよう true で初期化されているので、これはループ条件であって「データがあるか」のチェックではありません。クエリを投げるべきかどうかの判断に使うと、常にイエスと答えます。

エラーはページ単位で表面化し、部分的な読み取りは実在する状態ですNextPage は他の呼び出しと同じくラップされた smithy のエラーを返すので、文字列一致ではなく *types.ProvisionedThroughputExceededException などに対して errors.As でアンラップしてください。スニペットの log.Fatalf は、すでに集めたページを捨ててしまいます。サービスの中では通常、items を保持してどこまで進めたかを報告したいはずです。

パーティションを逆向きに読む唯一の方法が ScanIndexForward です。ソートキーの降順には ScanIndexForward: aws.Bool(false) を設定します。それ以外の「並べ替え」はありません。順序はソートキーから来るもので、別の順序が必要なら別のインデックスが必要です。IndexName: aws.String("...") はクエリ全体をそのインデックスに移します。

2 つのパッケージがこれを短くしますgithub.com/aws/aws-sdk-go-v2/feature/dynamodb/expressionexpression.Key("Artist").Equal(expression.Value("Arturo Sandoval")) からキー条件と 2 つのプレースホルダーマップを組み立て、手書きの #hashKey/:hashKeyValue の組と、それに伴う予約語のリスクを取り除きます。attributevalue.UnmarshalListOfMaps(page.Items, &songs) は 1 ページをそのまま []Song に変えます。

ビジュアルに行う

2 つのプレースホルダーマップこそ、手で打つより生成する価値のある部分です。無料の DynamoDB 式ビルダーは、対応する ExpressionAttributeNamesExpressionAttributeValues とともにキー条件を組み立て、Go のリテラルとして出力するので、名前と値がずれることはありません。

自分のテーブルに対してクエリを実行するには — キー条件のフォーム、スクロールに合わせてページ送りするグリッド、リクエストを Go としてコピーし直す機能 — DynoTable をダウンロードしてください。

関連する例

参考資料

2026-07-28 に、go1.26.5 と aws-sdk-go-v2/service/dynamodb v1.62.1 を用い、約 60 KB のアイテム 30 件からなるパーティションに対して、ポート 9000 の DynamoDB Local(amazon/dynamodb-local)で測定しました。ページごとの件数とキャパシティの読み取り値はそのままの出力です。DynamoDB Local はドキュメントどおりの丸め規則を適用します。絶対値は挙動の形を示すデモとして扱い、サイジングの前に実際のサービスで測定してください。

このリクエストをビジュアルに組み立てる

この操作を無料の DynamoDB クエリビルダーで組み立て — キー条件、フィルタ、インデックス、Limit、ソート順、ページネーションループ — 実行可能な SDK v3・CLI・boto3 のプログラムとしてコピーして戻れます。

DynamoDB クエリビルダーを開く

Console なしで DynamoDB を扱う

DynamoDB では実行できない本物の SQL(JOINs、GROUP BY、集計)を実行する高速な DynamoDB デスクトップクライアント。ビジュアル編集と、あなた自身の Bedrock キーで動く AI エージェントを備えています。

30日間無料トライアル、クレジットカード不要 — その後は期限のない Free プラン。