初級読了 3 分

DynamoDB で COUNT、SUM、集計を行う方法

DynamoDB には組み込みの集計がちょうど 1 つだけあります。Select=COUNT によるマッチした アイテムのカウントです。ネイティブの SUMAVGMINMAX はありません。しかも 取得できる カウントですら、数えたすべてのアイテムを読み取り(そして課金し)ます。 このガイドでは、実際に何がサポートされているか、人々が頼りにする近似手段、そして必要な ときに COUNT/SUM/AVG を本当にテーブルに対して実行する方法を扱います。

DynamoDB は SUM、COUNT、集計関数を実行できるか?

ほとんどできません。DynamoDB の唯一の組み込み集計は Select=COUNT で、マッチしたアイテムのカウントを返しますが、それでもすべてのアイテムを読み取り(そして課金し)ます。ネイティブの SUMAVGMINMAX はなく、PartiQL もそれらを追加しません。GROUP BY を伴う本物の集計には、アプリ内で畳み込むか、カウンターを維持するか、DynoTable の Workbench で SQL を実行します。

  • Select=COUNT はマッチしたアイテムの数を返しますが、DynamoDB はそれを算出するために すべてのアイテムを読み取ります — 安い「カウント」コストではなく、Scan/Query の完全な 読み取りコストを支払います。
  • ネイティブの SUMAVGMINMAX はない。 DynamoDB の読み取り操作はアイテムを 返すもので、それらを 1 つの数値に畳み込みはしません。PartiQL も集計を追加しません。
  • DescribeTable.ItemCount は無料ですが 近似値で、「およそ 6 時間ごとに」しか 更新されません — ダッシュボードのタイルには十分ですが、正確さが必要なものには誤りです。
  • 正確な COUNT/SUM/AVG/MIN/MAXGROUP BY 付き)には、アプリ内で集計するか、 カウンターを維持するか、DynoTable の SQL Workbench(後述)で実行します。

アイテムを数える:Select=COUNT

QueryScan はどちらも Select パラメータを受け付けます。それを COUNT に設定 すると、レスポンスはアイテムの代わりにカウントを運びます。

aws dynamodb scan \
  --table-name Orders \
  --select COUNT \
  --filter-expression "#s = :open" \
  --expression-attribute-names '{"#s":"status"}' \
  --expression-attribute-values '{":open":{"S":"OPEN"}}'

レスポンスは 2 つの数値を与えます(AWS: Counting the items in the results)。

  • Count — 「フィルタ式(もしあれば)が適用された に残るアイテムの数」。
  • ScannedCount — 「ScanFilter が適用される に評価されたアイテムの数」。 フィルタがなければ、ScannedCountCount と同じです。

だけを持っていて、その中の重複を数える必要があるなら、渡す 条件 + フィルタはまさに DynamoDB 式ビルダー が 生成するもの — 上記の FilterExpressionExpressionAttributeNames/Values マップ、 そして Query で 1 つのパーティション内を数えるときの KeyConditionExpression — であり、JSON を手でエスケープする必要はありません。

大きなテーブルを数える人々を悩ませる、もう 2 つの落とし穴。

  • 1 MB のページ制限は依然として適用される。Scan の結果セットのサイズが 1 MB を 超える場合、ScannedCountCount は総アイテム数の部分的なカウントのみを表します」 (AWS Scan ドキュメント)。 各レスポンスの LastEvaluatedKey を次のリクエストの ExclusiveStartKey として渡し、 実際の数値を得るために累計を保持しながらページ送りする必要があります — これは DynamoDB のページネーション で扱うのと同じループです。
  • 絞った QueryScan に勝る。 Query に対する Select=COUNT は、テーブル全体では なく、対象パーティション内のアイテムだけを計測します。パーティションキーを固定できるなら (ベーステーブルでも GSI でも)、そこで数えましょう — カウントに適用された Query と Scan のコスト差です。

Select=COUNT vs ItemCount(そしてなぜそれが古いのか)

DescribeTableItemCount(と TableSizeBytes)を無料で、読み取りコストなしで返します。 落とし穴は API リファレンスそのもの にあります。「DynamoDB はこの値をおよそ 6 時間ごとに更新します。最近の変更はこの値に 反映されていない場合があります」。つまりテーブルの実際の状態からかなり遅れることがあります。

Select=COUNTDescribeTable.ItemCount
正確さ正確(マッチしたセットについて)近似
鮮度ライブおよそ 6 時間ごとに更新
コスト数えた各アイテムを読み取り・課金無料(メタデータ)
サブセットのフィルタ / カウント可(フィルタ式)不可 — テーブル全体のみ

ItemCount は「このテーブルはどれくらい大きいか」というざっくりした感覚のチェックや ダッシュボードのタイルに使いましょう。正確な、フィルタされた、または最新の数値が必要な ときは Select=COUNT を使い、読み取りコストを受け入れましょう。本当にライブで無料の ものが必要なら、自分でカウンターを追跡してください(下の 集計パターン を参照)。

なぜネイティブの SUM/AVG/MIN/MAX がないのか

DynamoDB の読み取り操作はアイテムを返します。結果セットをスカラーに畳み込むクエリ プランナーがないため、SUMAVG を計算する対象がそもそもありません。API が提供する 唯一の畳み込みはカウントで、Select=COUNT を介します。

PartiQL はこれを変えません。 PartiQL SELECT の文法SELECT {{expression}} [, …] FROM {{table}}[.{{index}}] [WHERE …] [ORDER BY {{key}} …] で、ここで expression は「* ワイルドカード、または 1 つ以上の属性名やドキュメントパスの 射影リストから形成される射影」です。その文法には集計関数も GROUP BY 句もありません — そして ORDER BY{{key}} を取り、「返される結果を並べ替えるために使うハッシュキー またはソートキー」と文書化されています。すべての PartiQL SELECT は依然として GetItemQueryScan にコンパイルされるため、SELECT SUM(total) FROM "Orders" は単純に表現 できません。(PartiQL の上限については PartiQL vs SQL で詳述。)

集計パターン(カウンター、Streams、アプリ側)

DynamoDB が代わりに集計してくれないため、確立されたパターンは作業を別の場所に押しやります。

  • 維持されるカウンターアイテム。 専用のアイテム(例:PK = "STATS#orders")を保持し、 書き込みごとに UpdateItem で数値属性に ADD します。集計の読み取りはそれから単一の GetItem になります — 正確で安いですが、インクリメントのロジック、その整合性、そして 1 つのカウンターが叩かれ続けたときの競合を自分で所有します。
  • がアグリゲーターに供給する。 ストリームを有効にし、 アイテムが変わるたびに実行中の合計(カウント、sum)を更新する Lambda に配線します。 AWS Streams ドキュメント によれば、ストリームの StreamViewType を設定して各レコードが NEW_AND_OLD_IMAGES — 「アイテムの新旧両方のイメージ」 — を運ぶようにでき、これは再スキャンなしで SUM 型の 集計を最新に保つのに十分です。ストリームレコードは 24 時間の寿命の対象なので(「シャード 内のストリームレコードは 24 時間後に自動的に削除されます」)、コンシューマーは追いつく 必要があります。
  • アプリ側での畳み込み。 マッチしたアイテムをページ送りし、自分のコードで SUM/AVG/MIN/MAX を累積します。正しいですが、毎回すべてのアイテムを読み取り (そして課金し)ます — Select=COUNT と同じコストプロファイルに、データ転送が加わります。
  • 分析にオフロードする。 重いまたはアドホックな分析集計には、テーブルを S3 に エクスポートして Athena でクエリするか、ウェアハウスにストリームします。 AWS の S3 エクスポートドキュメント によれば、エクスポートは「読み取りキャパシティユニットを消費せず」、「Athena などの AWS サービスを使って分析や複雑なクエリを実行」できます — リクエストごとの集計から卒業したら、 AWS が推奨するパスです。

それぞれが、書き込み時の帳簿付け(カウンター、Streams)または読み取り時のコスト(アプリ側 スキャン)と引き換えに、シンプルさを得ています。DynamoDB 自身に SUM を無料で計算させる パターンはありません。このトレードオフのグルーピング版 — テーブル全体ではなく キーごとに 集計する — は、それ自身のガイドです:DynamoDB GROUP BY

DynoTable の SQL Workbench で COUNT/SUM/AVG を実行する

ページ送りするスキャンループや Lambda を書かずに、ただ答えが欲しいだけのとき — 「OPEN の注文はいくつあり、その合計はいくらか」 — DynoTable の SQL Workbench は本物の 集計を実行します。DynamoDB の実際の Query/Scan ランタイムを通してテーブルを マテリアライズし、その上で単一の SELECT を実行します — 集計、GROUP BYHAVINGDISTINCTDynamoDB のアクセスパターンのルールの中の SQL です。

-- Runs in the DynoTable Workbench (NOT in PartiQL):
SELECT status,
       COUNT(*)        AS orders,
       SUM(total)      AS revenue,
       AVG(total)      AS avg_order,
       MIN(total)      AS smallest,
       MAX(total)      AS largest
FROM orders
GROUP BY status
ORDER BY revenue DESC

これは計算された集計に対する COUNTSUMAVGMINMAXGROUP BYORDER BY であり — そのいずれも DynamoDB や PartiQL は表現できません(PartiQL の ORDER BY はキー 属性に限られます) — それを 1 つのステートメントで。これは DynamoDB のための SQL と同じ分析的なくさびです。完全なグルーピングの話は DynamoDB GROUP BY を参照してください。

Workbench は Postgres のふりをするのではなく、その下のアクセスモデルについて正直です。

  • 行は依然として DynamoDB の実際の Query/Scan を通して来ます。テーブル全体に対する GROUP BY は、その下では依然として Scan です — Workbench はそのコストを隠すのではなく 表面化させます。これは同じ Query と Scan のトレードオフです。
  • 集計は、行が着地した後、マテリアライズされたスカラー属性に対して実行されます。

よくある質問

スキャンせずに DynamoDB のアイテムを数えられますか? 厳密にはできません。正確で最新のカウントには、アイテムを読み取る必要があります — Select=COUNT は依然として数えた各アイテムを計測します。スキャンなしの選択肢は、近似の DescribeTable.ItemCount(およそ 6 時間ごとに更新)か、書き込みごとに自分で維持する カウンターアイテムだけです。

GSI でアイテムを数えるには? インデックスに対して Select=COUNT を付けて Query(または Scan)を実行します。絞った GSI パーティションを介したカウントは、そのインデックスパーティション内のアイテムだけを 読むため、ベーステーブルをスキャンするよりはるかに安価です — 必要なカウントに合わせて インデックスをモデル化しましょう。

DescribeTable.ItemCount は正確ですか? 近似です。 API リファレンス は、DynamoDB が ItemCountTableSizeBytes を「およそ 6 時間ごとに」更新し、「最近の 変更はこの値に反映されていない場合がある」と述べています。正確または最新の数値が重要な 場所では使わないでください。

DynamoDB は SUMAVG を実行できますか? ネイティブにはできず、PartiQL でもできません — PartiQL SELECT の文法 には集計関数がありません。アプリケーション内で集計するか、カウンターを維持するか (任意で DynamoDB Streams 経由で)、DynoTable の SQL Workbench で SUM/AVG を実行します。

CountScannedCount の違いは? ScannedCount はフィルタ前に DynamoDB が評価したアイテム数で、Count はその後に残る数です。 フィルタ式がなければ両者は等しくなります。両者の大きな差は、非効率なカウントを意味します。


スキャンループを書かずに DynamoDB のデータを合計、平均、グループ化する必要がありますか? DynoTable をダウンロード して Workbench タブで実行しましょう。まずクライアントを 比較しますか?プレーンな DynamoDB GUI との位置づけをご覧ください。

更新日