初級読了 2 分

DynamoDB の Query と Scan

Query はパーティションキーで単一のアイテムコレクションを読み取り(オプションで ソートキーで絞り込み)、Scanテーブル全体 を読んでから後でフィルタします。 API では似て見えますが、課金とスケールはまったく異なります。

DynamoDB で Query と Scan をいつ使い分けるべきか?

必要なパーティションを名指しできる場合はいつでも Query を使いましょう — 1つのアイテムコレクションを読み取り、マッチしたアイテム分だけ課金されます。Scan は1回限りのエクスポートや小さなテーブルにのみ使用してください。Scan はすべてのアイテムを読み取り、FilterExpression が実行される前にテーブル全体を課金します。実際のデータでは、Query が勝ります。

  • Query は対象を絞ります。マッチしたパーティション内のアイテムに対して支払います。
  • Scan は網羅的です。すべてのアイテムを読むために支払い、その後ほとんどを FilterExpression で捨てます。フィルタは読み取りが計測された に動きます。

ある程度の規模のテーブルでは、フィルタ付きの Scan は典型的な「なぜ請求額が巨大で、 レイテンシが RDS より悪いのか」という落とし穴です。

並べて比較

QueryScan
読み取り1つのパーティション(PK で)テーブル内の すべての アイテム
課金キャパシティパーティション内でマッチしたアイテムフィルタ前の テーブル全体
FilterExpression読み取り後に適用 — それでも読み取り分は課金される同様 — フィルタはコストを削らない
レイテンシテーブルが成長しても一定テーブルサイズとともに増加
ページネーション1 MB/ページ → LastEvaluatedKey1 MB/ページ。並列化可能
用途既知のアクセスパターン1回限りのエクスポート、小さな設定テーブル

肝心な罠: FilterExpression は、両方の操作で、DynamoDB が読み取りを計測した に 動きます。「10行を返す」Scan が、100万行の読み取り分を課金することがあります — フィルタは便宜であって、決してコスト管理ではありません。

Query を使う

Query  PK = "USER#42"  AND  SK begins_with "ORDER#"

よくあるアクセスパターンに答えるために Scan に手が伸びるようなら、それはモデリングの サインです。グローバルセカンダリインデックス を追加して、その パターンを Query にしてください。

選択は1つの問いに帰着します — 必要なパーティションを名指しできますか?

YesNoYesNoAccess patternPartition key known?Query reads one partitionCan a GSI key it?Add a GSIScan reads the whole table

キーがわかっていれば Query し、わからなければ GSI を追加して Query にできるようにし、 どのキーも当てはまらないときだけ Scan にフォールバックします。

Scan で問題ないとき

1回限りのエクスポート、小さな設定テーブル、そしてテーブル全体を意図的にページ送り するバックグラウンドジョブです。本当にすべてを読まなければならないときは、 Segment/TotalSegments を使って Scan をワーカー間で分割してください (並列スキャン)。

DynamoDB に対する反射的な SELECT * FROM table は、PartiQL の衣をまとった同じ アンチパターンです — Scan にコンパイルされます。本当にアイテム横断の分析 (GROUP BYJOIN、集計)が必要なときは、DynoTable の SQL Workbench が、テーブルを 叩く代わりに、限定された結果セットに対してクライアント側でそれらを実行します。

どちらのパターンがどれだけの読み取りユニットを消費するかを キャパシティ計算ツール で見積もり、 DynoTable を試して これらのクエリを自分のテーブルに対して実行し、 確認してください。

更新日