上級読了 3 分

DynamoDB のパラレルスキャン

パラレルスキャンは、1つの Scan を N 個の独立した Scan リクエストに分割し、それぞれが テーブルの Segment を担うことで、複数のワーカーが同時に読み取るようにします。テーブル 全体を、1つのパーティションのスループットが許すより速く読む——Scan API が提供する唯一の 方法です。

DynamoDB のパラレルスキャンとは何ですか?

DynamoDB のパラレルスキャンは、1つの Scan を N 個の独立したリクエストに分割し、それぞれが SegmentTotalSegments を介してテーブルの 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 0Scan  Segment=0  TotalSegments=4
Worker 1Scan  Segment=1  TotalSegments=4
Worker 2Scan  Segment=2  TotalSegments=4
Worker 3Scan  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=falseScan  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)

sensor-readings テーブルパーティションキーをハッシュSegment 0DEVICE#a83fDEVICE#1c20Segment 1DEVICE#9be4Segment 2(空)

その帰結:セグメントは不均一です。あるワーカーは数百万の測定値を持つおしゃべりな デバイス3台を所有し、別のワーカーは空の切れ端を引くかもしれません。パーティションキーが 固まっていると、TotalSegments を高くしても助けになりません——ホットな1台を待つ遊休の ワーカーを増やすだけです。均等なキー分布こそが、この分散を報われるものにします。

実行前に読み取りコストを見る

パラレルスキャンはスループットのイベントであり、タダ飯ではありません。正直な問いは 「このテーブル全体のどれだけを、これから読もうとしているのか?」です。DynoTable は テーブル全体の読み取りを実行する前に、おおよそのテーブルサイズとアイテム数に加えて 読み取りキャパシティの警告を表示する確認ダイアログでゲートし、読み取りの実行中は スキャン済みアイテムの進捗をライブでストリームします。夜間ジョブが不意打ちにならない ようにです。

落とし穴と、手を出すべきでないとき

  • スループットの崖。 TotalSegments が高いスキャンは、数秒でテーブルの読み取り キャパシティを丸ごと消費し、ライブトラフィックを飢えさせかねません。ユーザーに提供中の テーブルでは、Limit パラメータで各ワーカーを絞るか、オフピークにスキャンしましょう。 (AWS: Parallel scan)
  • アクセスパターンには依然として誤った道具。 パラレルスキャンは、意図的なテーブル 全体のジョブ——エクスポート、バックフィル、マイグレーション——のためのものです。繰り返し 発生するクエリに答えるために使おうとしているなら、それはモデリングの兆候です。 GSIを追加してQueryにしましょう。
  • PartiQL の SELECT * は同じスキャンの変装。 逐次的な Scan にコンパイルされます。 本当にアイテム間の分析——GROUP BYJOIN、集計——が必要なときは、DynoTable の SQL Workbench が、テーブルを叩く代わりに、上限のある結果セットに対してそれらをクライアント 側で実行します。
  • 強い整合性は請求を倍にする。 Scan はデフォルトで のある読み取りです。エクスポートでは、各ページが最新の書き込みを反映しなければならない のでない限り ConsistentRead=false のままにしましょう——そして強い整合性のあるスキャン でさえ数分にわたることに注意してください。時点スナップショットではありません (それには PITR/エクスポートを使いましょう)。

次のステップ

日々の読み取りが決してスキャンを必要としないようキーをモデル化しましょう—— シングルテーブル設計Query vs Scanから始めます。テーブル全体のジョブが本当に正しい 選択のときは、DynoTable を試して、事前のサイズとコストの警告つき、そして ライブのスキャン進捗つきでテーブル全体の読み取りを実行しましょう。

更新日