DynamoDB のパラレルスキャン
パラレルスキャンは、1つの Scan を N 個の独立した Scan リクエストに分割し、それぞれが
テーブルの Segment を担うことで、複数のワーカーが同時に読み取るようにします。テーブル
全体を、1つのパーティションのスループットが許すより速く読む——Scan API が提供する唯一の
方法です。
DynamoDB のパラレルスキャンとは何ですか?
DynamoDB のパラレルスキャンは、1つの Scan を N 個の独立したリクエストに分割し、それぞれが
Segment と TotalSegments を介してテーブルの Segment を担うことで、複数のワーカーが
並行して読み取ります。テーブル全体を、単一パーティションのスループットが許すより速く読む——
Scan API が提供する唯一の方法です。ただしそれでも全読み取りなので、スキャンしたすべての
アイテムに支払います。
- 逐次的な
Scanは一度に1つのパーティションを読む。その速度は、テーブルがどれだけ 大きくても、単一パーティションのスループットで頭打ちになります。 Segment+TotalSegmentsが読み取りをシャーディングし、TotalSegments個の ワーカーに分けます。各ワーカーは自分の切れ端を並列にスキャンします。- DynamoDB はをハッシュしてセグメントを割り当てる ので、切れ端は偏りうります。ワーカーを増やしても必ずしも速くなりません。
- それでも
Scanです。すべてのアイテムを読むのに支払い、太ったパラレルスキャンは ライブトラフィックの足下からテーブルのスループットを吸い上げかねません。
逐次的な Scan が遅い理由
SQL から来ると、テーブル全体の読み取りは1つのストリーミング操作のように感じます。DynamoDB
ではそうではありません。テーブルのデータは多くの物理パーティションにまたがって存在しますが、
単一の Scan はそれらを一度に1つずつ、1ページ 1 MB で歩きます。
つまり素の Scan は、たとえテーブルが空きキャパシティを持つ数十のパーティションに広がって
いても、ある瞬間には1つのパーティションのスループット予算からしか引き出せません。テーブル
が大きいほど、這うのに長くかかります。
(AWS: Parallel scan)
Segment と TotalSegments で読み取りを分割する
パラレルスキャンはこのボトルネックを解消します。ワーカー数を選び、TotalSegments を
その数に設定し、各ワーカーに 0 始まりの別々の Segment を与えます。各ワーカーは自分の
Scan を発行し、DynamoDB はそれらを並行して処理します。
Worker 0 → Scan Segment=0 TotalSegments=4
Worker 1 → Scan Segment=1 TotalSegments=4
Worker 2 → Scan Segment=2 TotalSegments=4
Worker 3 → Scan Segment=3 TotalSegments=4
各ワーカーはやはり LastEvaluatedKey で独立してページングします——最初のページから
最後まで、自分のセグメントを所有します。アプリケーションが4つのストリームを縫い合わせて
戻します。これで1つ分ではなく4つのパーティション分のスループットを一度に読んでいます。
具体例:夜間エクスポート
テレメトリテーブル sensor-readings を運営しているとします。各アイテムはフィールド
デバイスからの1つの測定値です。
PK = "DEVICE#a83f" (partition key — the device id)
SK = "TS#2026-06-22T03:14" (sort key — ISO timestamp)
batteryMv = 3120
tempC = 41.8
firmwareTag = "fw-7.2.1"毎晩、cron ジョブがテーブル全体を分析ウェアハウス向けに S3 へダンプします。80 GB の
逐次的な Scan は何時間もかかり、プロビジョンド読み取りキャパシティにほとんど食い込み
ません。そこで8つのワーカーに分散させます。
Scan sensor-readings Segment=0 TotalSegments=8 ConsistentRead=false
…
Scan sensor-readings Segment=7 TotalSegments=8 ConsistentRead=false
8つのワーカー、8つのセグメント、1回のテーブル読み取りがおよそ8倍速く。最近の測定値だけ
必要なら、FilterExpression を追加して古いタイムスタンプを行がワイヤーに乗る前に落とします。
その式を式ビルダーで組み立てて調べましょう。
FilterExpression: begins_with(SK, :today)DynamoDB がアイテムをセグメントに割り当てる仕方
ここが人をつまずかせる部分です。DynamoDB は各アイテムを、パーティションキーをハッシュ してセグメントに割り当てます——行数でも、バイト数でもありません。
ですから同じ PK を共有するアイテムはすべて同じセグメントに落ちます。sensor-readings
では、DEVICE#a83f のすべての測定値が1つのワーカーに行きます。そのデバイスがいくつの
タイムスタンプを持とうと、そのがどれだけ大きかろうと。
(AWS: Parallel scan)
その帰結:セグメントは不均一です。あるワーカーは数百万の測定値を持つおしゃべりな
デバイス3台を所有し、別のワーカーは空の切れ端を引くかもしれません。パーティションキーが
固まっていると、TotalSegments を高くしても助けになりません——ホットな1台を待つ遊休の
ワーカーを増やすだけです。均等なキー分布こそが、この分散を報われるものにします。
実行前に読み取りコストを見る
パラレルスキャンはスループットのイベントであり、タダ飯ではありません。正直な問いは 「このテーブル全体のどれだけを、これから読もうとしているのか?」です。DynoTable は テーブル全体の読み取りを実行する前に、おおよそのテーブルサイズとアイテム数に加えて 読み取りキャパシティの警告を表示する確認ダイアログでゲートし、読み取りの実行中は スキャン済みアイテムの進捗をライブでストリームします。夜間ジョブが不意打ちにならない ようにです。
落とし穴と、手を出すべきでないとき
- スループットの崖。
TotalSegmentsが高いスキャンは、数秒でテーブルの読み取り キャパシティを丸ごと消費し、ライブトラフィックを飢えさせかねません。ユーザーに提供中の テーブルでは、Limitパラメータで各ワーカーを絞るか、オフピークにスキャンしましょう。 (AWS: Parallel scan) - アクセスパターンには依然として誤った道具。 パラレルスキャンは、意図的なテーブル 全体のジョブ——エクスポート、バックフィル、マイグレーション——のためのものです。繰り返し 発生するクエリに答えるために使おうとしているなら、それはモデリングの兆候です。 GSIを追加してQueryにしましょう。
- PartiQL の
SELECT *は同じスキャンの変装。 逐次的なScanにコンパイルされます。 本当にアイテム間の分析——GROUP BY、JOIN、集計——が必要なときは、DynoTable の SQL Workbench が、テーブルを叩く代わりに、上限のある結果セットに対してそれらをクライアント 側で実行します。 - 強い整合性は請求を倍にする。
Scanはデフォルトで のある読み取りです。エクスポートでは、各ページが最新の書き込みを反映しなければならない のでない限りConsistentRead=falseのままにしましょう——そして強い整合性のあるスキャン でさえ数分にわたることに注意してください。時点スナップショットではありません (それには PITR/エクスポートを使いましょう)。
次のステップ
日々の読み取りが決してスキャンを必要としないようキーをモデル化しましょう—— シングルテーブル設計と Query vs Scanから始めます。テーブル全体のジョブが本当に正しい 選択のときは、DynoTable を試して、事前のサイズとコストの警告つき、そして ライブのスキャン進捗つきでテーブル全体の読み取りを実行しましょう。