クエリとソート
SQL の WHERE のように感じるから Scan にフィルターを足し、コンソールが一致する 20 行を見せているあいだにテーブル内の全アイテムを読んで支払う。そのパターンは、本番の請求でよくある DynamoDB のコスト上の驚きだ。
DynamoDB の読み取りはキー付きだ。Query は 1 つのアイテムコレクションをソートキー順に歩き、Scan はテーブル全体に触れる。
フィルター式はキャパシティが課金されたあとで行を捨てる。ページネーションは
LastEvaluatedKey を運ばなければならない。ソートは選んだインデックスのソートキー経由で行われ、アプリメモリ内の任意の列ではない。
このセクションで身につくこと
- 機能ごとに
QueryとScanを選び、パーティション範囲でその選択を防衛できる。 - アイテムを落としたり重複させたりせず、大きな結果セットをページできる。
- 可能ならキー条件でフィルターをかけ、読み取り後フィルターがまだいくら掛かるか知っている。
- ソート順をソートキーにエンコードし(数値順のためのゼロパディング、可変の値は GSI キーに)、どちらの方向にも読み取れる。
読む順番
- Query 対 Scan: 他の読み取りガイドが理解を前提にする課金の判断。
- ページネーション:
LastEvaluatedKey、ページループ、そしてLimitが「返された行数」ではない理由。 - フィルタリング戦略: キー条件対フィルター式。まずキーで絞り込み、キーで絞れない分だけをフィルターにかける。
- なぜ Scan は遅くて高いのか: テーブル成長対パーティション成長。ワンオフの
Scanが許容されるとき。 - 並列スキャン: バックフィルと移行のためのセグメント化
Scan。スループットのトレードオフ。 - ソートキー戦略: 順序がアプリ内ではなくディスク上にあるようソートキーを設計する。
- クエリの降順:
ScanIndexForwardと最新優先の読み取り。 - 可変ソートキー: キー属性を更新できない理由と、delete+put を避ける GSI ソートキーのパターン。
- ゼロパディングソート: 固定幅の数値ソートキーで
TICKET#00042がTICKET#00100の前に並ぶようにする(パディングなしではTICKET#100がTICKET#42の前に並ぶ)。
PK = TEAM#billing と SK begins_with TICKET# の Query はそのチームのチケットだけを読む。千万アイテムのテーブル上で filter status = OPEN の Scan は、それでも千万アイテムすべての読み取りを課金する。
us-east-1 のオンデマンド定価では、4 KB の読み取りリクエストユニット 1 つは $0.000000125 だ。呼び出しあたりは些細だが、テーブル規模では大きい。料金計算機 が実際の読み取りレートとアイテムサイズで料金を見積もる。
並列スキャンは、バックフィルや移行のようにすべてのライブアイテムに触れなければならないジョブのために、Scan をワーカーへ分割する。増えるのは読み取りのレート であって総量ではない。請求額は逐次スキャンと同じで、早く届くだけだ。
幅の広い並列スキャンはライブトラフィックを枯渇させうる。S3 への定期的なダンプには、代わりに S3 へのエクスポートを使おう。テーブルではなく、ポイントインタイムリカバリのバックアップを読み取る。
ベーステーブルのソートキーを変えるには、テーブルでも、そのアイテムが現れるすべての
GSI でも、delete と put が必要になる。可変ソートキーのガイドは、代わりに変わりやすい値を GSI のソートキーに置き、普通の UpdateItem でそれを動かす。
ゼロパディングは、ソートキーが数値 ID を運ぶ文字列のときの辞書順を直す。
DynoTable で
無料の クエリビルダー は、完全な Query または
Scan リクエストを、ページネーションループ付きの実行可能なプログラム(12 の言語と
SDK)か、PartiQL ステートメントとして組み立てる。
アプリでは、ビジュアルなクエリビルダーでインデックスを選べ、その実行ボタンはキー条件に応じて Query か Scan と表示されるので、どちらの読み取りに支払おうとしているのかが分かる。
DynoTable をダウンロードして、ライブテーブルに対してこれらの読み取りを実行しよう。クエリコストのステータスバーは、Workbench と PartiQL タブで実行前のヒント(プランが分かるときは ~N items · ~R RCU)を出すので、思わぬ請求が実行前に表面化する。