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(ソートキー)でキー付けされ、BatteryPct や AltitudeM のような属性を持ちます。
1台のドローンの最新の読み取り値を要求します。
PK = "DRONE#A19F"
SK begins_with "2026-06-23"
ルーターがそれをどう扱うかは次のとおりです。下のリードインはリクエストを上から下へ追います — 1つの下向きの流れとして読んでください。
ルーターは DRONE#A19F をハッシュし、そのキーを所有するパーティションにマップし、そのパーティションのプライマリストレージノードへ読み取りを転送し、ノードがアイテムを返します。
鍵となる洞察: ハッシュは、テーブルが持つ数あるパーティションのうち 1つ を指します。ルーターは他のパーティションを決して見ないので、ドローン — そしてパーティション — を追加してもこのルックアップは遅くなりません。
パーティションが実際に何かを知る
パーティション はストレージとスループットの単位です。1つ1つに上限が課され(おおよそ 10 GB と固定された読み取り/書き込みキャパシティのスライス)、DynamoDB はパーティションがどちらかの上限を超えると分割します。あるパーティションキーを持つすべてのアイテムは 同じ パーティションに存在し、それが1つのパーティションキーへの Query を安くします。
各パーティションは、Availability Zone にまたいで広がる3つのストレージノードにレプリケートされます — 1つの プライマリ と2つの セカンダリ です。
| ノードの役割 | 担当 | 提供できる整合性 |
|---|---|---|
| プライマリ | すべての書き込み、強い整合性のある読み取り | 強い(自身の最新書き込みを見る) |
| セカンダリ | 結果整合性のある読み取り、フェイルオーバー | 結果整合性(プライマリに遅れうる) |
書き込みはプライマリに行き、それが耐久性を確認応答する前にセカンダリへレプリケートします。強い整合性のある 読み取りは最新の書き込みを反映するようプライマリへルーティングされます。結果整合性のある 読み取りは、まだ追いついていないセカンダリから提供されえます — コストは半分、おそらく古い。
地雷に名前を付ける: ホットなパーティションキー
ルーティングはパーティションキーの良し悪し次第です。ハッシュはキーを均等に広げるので、キーが 高カーディナリティ で均等なトラフィックなら、負荷はすべてのノードに広がります。どちらかの性質を壊すと ホットパーティション を得ます。
そのテレメトリを DroneId ではなく Region でキー付けするとします。今や us-east-1 のすべてのドローンが1つのパーティションキーを共有します — なので、それらすべての読み取りと書き込みが 同じ ノードにハッシュされます。ルーターは完璧に仕事をしています。あなたがフリート全体を単一パーティションのキャパシティに流し込んだだけです。
ルーターがノードを選ぶのを見ることはできませんが、うまくルーティングするキーを 設計 できます。式ビルダーでキー条件を構築するとき、PK = … の左に置くパーティションキーは、ルーターがハッシュする正確な値です — その値を高カーディナリティに保つことが、読み取りを別々のノードに保つことです。
これがアクセスパターンにどうつながるか
リクエストルーティングは、シングルテーブル設計のルールを譲れないものにするメカニズムです — パーティションキーを中心にモデリングするのは、パーティションキー こそ がアドレスだからです。それはまた、Query が Scan に勝つ理由でもあります — Query はルーターを通して1つのパーティションに当たりますが、Scan はすべてのパーティションを順に歩きます。
セカンダリインデックスは独自のパーティションと独自のルーティングを持ちます — GSI はその独自のパーティションキーでルーティングされ、ベーステーブルのものとは独立です。だから GSI は、テーブルがそうでなくてもホットになりうるのです。
次のステップ
1つではなく多くのノードへルーティングするキーを設計しましょう。式ビルダーで PK = … 条件をスケッチして、どの値がハッシュされるかを正確に見て、それからDynoTable をダウンロードして、それらのクエリを自分のテーブルに対して実行し、どのパーティションキーが実際にトラフィックを運ぶかを見ましょう。