DynamoDB の Query と Scan
Query はパーティションキーで単一のアイテムコレクションを読み取り(オプションで
ソートキーで絞り込み)、Scan は テーブル全体 を読んでから後でフィルタします。
API では似て見えますが、課金とスケールはまったく異なります。
DynamoDB で Query と Scan をいつ使い分けるべきか?
必要なパーティションを名指しできる場合はいつでも Query を使いましょう — 1つのアイテムコレクションを読み取り、マッチしたアイテム分だけ課金されます。Scan は1回限りのエクスポートや小さなテーブルにのみ使用してください。Scan はすべてのアイテムを読み取り、FilterExpression が実行される前にテーブル全体を課金します。実際のデータでは、Query が勝ります。
- Query は対象を絞ります。マッチしたパーティション内のアイテムに対して支払います。
- Scan は網羅的です。すべてのアイテムを読むために支払い、その後ほとんどを
FilterExpressionで捨てます。フィルタは読み取りが計測された 後 に動きます。
ある程度の規模のテーブルでは、フィルタ付きの Scan は典型的な「なぜ請求額が巨大で、
レイテンシが RDS より悪いのか」という落とし穴です。
並べて比較
| Query | Scan | |
|---|---|---|
| 読み取り | 1つのパーティション(PK で) | テーブル内の すべての アイテム |
| 課金キャパシティ | パーティション内でマッチしたアイテム | フィルタ前の テーブル全体 |
FilterExpression | 読み取り後に適用 — それでも読み取り分は課金される | 同様 — フィルタはコストを削らない |
| レイテンシ | テーブルが成長しても一定 | テーブルサイズとともに増加 |
| ページネーション | 1 MB/ページ → LastEvaluatedKey | 1 MB/ページ。並列化可能 |
| 用途 | 既知のアクセスパターン | 1回限りのエクスポート、小さな設定テーブル |
肝心な罠: FilterExpression は、両方の操作で、DynamoDB が読み取りを計測した 後 に
動きます。「10行を返す」Scan が、100万行の読み取り分を課金することがあります —
フィルタは便宜であって、決してコスト管理ではありません。
Query を使う
Query PK = "USER#42" AND SK begins_with "ORDER#"
よくあるアクセスパターンに答えるために Scan に手が伸びるようなら、それはモデリングの
サインです。グローバルセカンダリインデックス を追加して、その
パターンを Query にしてください。
選択は1つの問いに帰着します — 必要なパーティションを名指しできますか?
キーがわかっていれば Query し、わからなければ GSI を追加して Query にできるようにし、
どのキーも当てはまらないときだけ Scan にフォールバックします。
Scan で問題ないとき
1回限りのエクスポート、小さな設定テーブル、そしてテーブル全体を意図的にページ送り
するバックグラウンドジョブです。本当にすべてを読まなければならないときは、
Segment/TotalSegments を使って Scan をワーカー間で分割してください
(並列スキャン)。
DynamoDB に対する反射的な SELECT * FROM table は、PartiQL の衣をまとった同じ
アンチパターンです — Scan にコンパイルされます。本当にアイテム横断の分析
(GROUP BY、JOIN、集計)が必要なときは、DynoTable の SQL Workbench が、テーブルを
叩く代わりに、限定された結果セットに対してクライアント側でそれらを実行します。
どちらのパターンがどれだけの読み取りユニットを消費するかを キャパシティ計算ツール で見積もり、 DynoTable を試して これらのクエリを自分のテーブルに対して実行し、 確認してください。