上級読了 3 分

DynamoDB のリクエストルーティングの仕組み

送るすべての読み取りや書き込みは、まずステートレスな リクエストルーター のフリートに当たります。ルーターはパーティションキーをハッシュし、ハッシュをそのキーのデータを所有するストレージノードにマップし、そこへリクエストを転送します。その1ホップこそ、テーブルが千個のアイテムを持とうと10億個持とうと、キールックアップのコストが同じである理由です。

DynamoDB のリクエストルーティングはどのように機能しますか?

DynamoDB はすべてのリクエストを、パーティションキーをハッシュし、そのパーティションを所有する単一のストレージノードにハッシュをマップし、そこへ読み取りまたは書き込みを転送するステートレスなリクエストルーターフリートを通してルーティングします。ルーティングはキーのハッシュの純粋な関数なので、テーブルが千個のアイテムを持とうと10億個持とうと、1回のルックアップのコストは同じです。

  • リクエストルーターが正面玄関。 リクエストを受け取り、パーティションキーをハッシュし、そのパーティションを保持するストレージノードへルーティングするステートレスなフリートだ — スキャンも、テーブル全体の知識も不要。
  • パーティションキーがすべてを決める。 ルーティングはパーティションキーのハッシュの純粋な関数だ。同じキーは毎回同じノード — なので GetItem は O(テーブルサイズ) ではなく O(1) だ。
  • 1つのプライマリ、2つのセカンダリ。 書き込みはパーティションのプライマリノードに着地し、それが耐久性を得る前に Availability Zone をまたいで2つのセカンダリへレプリケートする。
  • 悪いキーは設計を台無しにする。 低カーディナリティや「ホット」なパーティションキーはトラフィックを1つのノードに流し込む — ルーティングは正常で、問題はあなたのキーだ。

ルーティングが解く問題から始める

SQL から来ると、クエリプランナーを思い描きます — 統計を読み、インデックスを選び、ときにスキャンします。コストは触れるデータ量に比例します。そのモデルは、どんなサイズでも1桁ミリ秒で答えなければならないキーバリューストアには合いません。

DynamoDB の答えは、単一アイテムのルックアップを検索ではなく 直接アドレス にすることです。パーティションキーはフィルタする列ではなく — データが物理的にどこに存在するか を計算するハッシュ関数への入力です。統計なし、プランナーなし。

それがリレーショナル思考から移るときに受け入れるトレードです — アドホックなクエリの柔軟性を手放し、代わりに定数時間のアドレッシングを得ます。

リクエストルーターに会う

リクエストが到着しても、ストレージへ直行はしません。リクエストルーター に当たります — サービス全体の前に立つ、ステートレスで水平にスケールするフリートです。(AWS re:Invent の "DynamoDB Deep Dive" セッションがこのフロントエンドのフリートを説明しています。

ルーターは3つのことをし、自身のデータは一切持ちません。

  • 認証と認可 を IAM に対して行う。
  • パーティションキーをハッシュ して、それを所有するパーティションを見つける。
  • そのパーティションのストレージノードへリクエストを 転送 する。

ルーターはステートレスなので、サービスは負荷下でそれらを増やします。どれもボトルネックにならず、どれも単一障害点になりません — 2007 年の Amazon Dynamo 論文が元のシステムを構築した、まさにその性質です。

1回の読み取りをルーターを通して追う

ドローンフリートのテレメトリテーブルを考えます。アイテムは DroneId(パーティションキー)と ReadingTs(ソートキー)でキー付けされ、BatteryPctAltitudeM のような属性を持ちます。

1台のドローンの最新の読み取り値を要求します。

PK = "DRONE#A19F"
SK begins_with "2026-06-23"

ルーターがそれをどう扱うかは次のとおりです。下のリードインはリクエストを上から下へ追います — 1つの下向きの流れとして読んでください。

クライアント: GetItemPK = DRONE#A19Fリクエストルーター(ステートレスなフリート)Hash(DRONE#A19F) キースペースのスロットスロット キーを所有するパーティションにマップそのパーティションのプライマリノードアイテムを読むDRONE#A19F

ルーターは DRONE#A19F をハッシュし、そのキーを所有するパーティションにマップし、そのパーティションのプライマリストレージノードへ読み取りを転送し、ノードがアイテムを返します。

鍵となる洞察: ハッシュは、テーブルが持つ数あるパーティションのうち 1つ を指します。ルーターは他のパーティションを決して見ないので、ドローン — そしてパーティション — を追加してもこのルックアップは遅くなりません。

パーティションが実際に何かを知る

パーティション はストレージとスループットの単位です。1つ1つに上限が課され(おおよそ 10 GB と固定された読み取り/書き込みキャパシティのスライス)、DynamoDB はパーティションがどちらかの上限を超えると分割します。あるパーティションキーを持つすべてのアイテムは 同じ パーティションに存在し、それが1つのパーティションキーへの Query を安くします。

各パーティションは、Availability Zone にまたいで広がる3つのストレージノードにレプリケートされます — 1つの プライマリ と2つの セカンダリ です。

ノードの役割担当提供できる整合性
プライマリすべての書き込み、強い整合性のある読み取り強い(自身の最新書き込みを見る)
セカンダリ結果整合性のある読み取り、フェイルオーバー結果整合性(プライマリに遅れうる)

書き込みはプライマリに行き、それが耐久性を確認応答する前にセカンダリへレプリケートします。強い整合性のある 読み取りは最新の書き込みを反映するようプライマリへルーティングされます。結果整合性のある 読み取りは、まだ追いついていないセカンダリから提供されえます — コストは半分、おそらく古い。

地雷に名前を付ける: ホットなパーティションキー

ルーティングはパーティションキーの良し悪し次第です。ハッシュはキーを均等に広げるので、キーが 高カーディナリティ で均等なトラフィックなら、負荷はすべてのノードに広がります。どちらかの性質を壊すと ホットパーティション を得ます。

そのテレメトリを DroneId ではなく Region でキー付けするとします。今や us-east-1 のすべてのドローンが1つのパーティションキーを共有します — なので、それらすべての読み取りと書き込みが 同じ ノードにハッシュされます。ルーターは完璧に仕事をしています。あなたがフリート全体を単一パーティションのキャパシティに流し込んだだけです。

ルーターがノードを選ぶのを見ることはできませんが、うまくルーティングするキーを 設計 できます。式ビルダーでキー条件を構築するとき、PK = … の左に置くパーティションキーは、ルーターがハッシュする正確な値です — その値を高カーディナリティに保つことが、読み取りを別々のノードに保つことです。

これがアクセスパターンにどうつながるか

リクエストルーティングは、シングルテーブル設計のルールを譲れないものにするメカニズムです — パーティションキーを中心にモデリングするのは、パーティションキー こそ がアドレスだからです。それはまた、QueryScan に勝つ理由でもあります — Query はルーターを通して1つのパーティションに当たりますが、Scan はすべてのパーティションを順に歩きます。

セカンダリインデックスは独自のパーティションと独自のルーティングを持ちます — GSI はその独自のパーティションキーでルーティングされ、ベーステーブルのものとは独立です。だから GSI は、テーブルがそうでなくてもホットになりうるのです。

次のステップ

1つではなく多くのノードへルーティングするキーを設計しましょう。式ビルダーPK = … 条件をスケッチして、どの値がハッシュされるかを正確に見て、それからDynoTable をダウンロードして、それらのクエリを自分のテーブルに対して実行し、どのパーティションキーが実際にトラフィックを運ぶかを見ましょう。

更新日