DynamoDB のストレージ内部の仕組み
DynamoDB は、値がソートされた木であるハッシュテーブルで、3つのアベイラビリティーゾーンのマシンにまたがって 広がっています。ほぼすべての仕事を2つのデータ構造が行います。 に対する ハッシュがマシンを選び、 に対する B-tree がその中でアイテムを順序付けます。
DynamoDB はどのようにデータを格納するのか?
DynamoDB はあなたのデータを、値がソートされた B-tree である巨大な分散ハッシュテーブルとして格納します。 に対するハッシュが1つのストレージノードを選び、B-tree がその中で ソートキーによってアイテムを順序付けます。すべての書き込みはリーダーへ向かい、リーダーは3つの アベイラビリティーゾーンにまたがる2つのピアに複製し、クォーラム(3つのレプリカのうち2つ)がそれを持った時点で 確認応答します。
- パーティションキーは検索されるのではなく、ハッシュ化される。 DynamoDB はあなたの
PKに対して ハッシュ関数を実行し、そのパーティションを保持するストレージノード(大きなコレクションが分割されれば、複数の ノード)を見つけます — テーブルサイズに関わらず O(1) のジャンプです。 - ソートキーは B-tree に住む。 パーティション内では、アイテムはソートキーで順序付けられた B-tree に格納
されます — 文字列なら UTF-8 バイト順、Number キーなら数値順 — これが、範囲読み取り(
begins_with、between)が 安価でScanがそうでない理由です。 - すべての書き込みは確認応答の前にクォーラムでコミットする。 書き込みはリーダーへ向かい、リーダーは他の AZ の
2つのピアに複製し、クォーラム(3つのレプリカのうち2つ)がそれを持った時点で確認応答します — 耐久性は
PutItemが返る前に買われます。 - これがアクセスパターンのルールが存在する理由。 ハッシュしてから木、というのはキーで読むときにだけ高速です。 キーがなければ、高速な経路もありません — 木をスキャンすることに逆戻りします。
API ではなく、データ構造から始める
SQL から来ると、テーブルをディスク上の行と、インデックスを選ぶクエリプランナーとして思い描きます。 DynamoDB にはプランナーがありません。ストレージのレイアウト こそが 契約です — 何が高速で何が落とし穴かは、 どちらも2つの構造からまっすぐ導かれます。
巨大な分散マップを思い描きましょう。キーはあなたのパーティションキーのハッシュです。値は単一のアイテムでは ありません — それは、そのパーティションキーを共有し、ソートキーで順序付けられたアイテムの、まるごと1本の B-tree です。
それ以外のすべて — クエリのセマンティクス、10 GB の警告、なぜキーがないと Scan を強いられるのか — は、
その1文の帰結です。
パーティションキーをハッシュ化してノードを見つける
リクエストが到着すると、DynamoDB はパーティションキーの値に内部のハッシュ関数を適用します。そのハッシュは 決定論的に1つのストレージノード — それらのアイテムを所有する物理パーティション — にマッピングされます。 AWS はこれを、テーブルサイズに関わらず定数時間のキー参照を支える仕組みとして文書化しています。
それが O(1) のステップです。10 TB のテーブルと 10 KB のテーブルは、場所を特定する コストは同じです。 ハッシュし、ジャンプし、完了。ノードを見つけるためのインデックススキャンも、統計情報も、プランもありません。
落とし穴は裏側にあります。パーティションキーを供給しないと、DynamoDB にはジャンプするノードがありません —
すべてのパーティションを歩き回らなければなりません。それが Scan であり、O(1) とテーブル全体の読み取りとの
違いです。
リクエストはちょうど1つのパーティションにハッシュ化され、それからそのパーティションのソートキー B-tree を アイテムまで下ります — テーブルの走査ではなく、2つの安価なステップです。
パーティションごとの B-tree でアイテムを順序付ける
単一のパーティション内では、アイテムはヒープではありません。それらはソートキーでキー付けされた B-tree に
保持され、辞書順に順序付けられています。B-tree の探索は O(log n) で、決定的に重要なのは、その n が
テーブル全体ではなく、1つの パーティション内のアイテム数であることです。
これが、ソートキーの範囲読み取りが安価である理由のすべてです。各デバイスの測定値が1つのパーティションキーの 下に住むフリートテレメトリのテーブルを取り上げます。
| PK | SK |
|---|---|
| PK = DEVICE#a91 | SK = READING#2026-06-23T08:00Z |
| PK = DEVICE#a91 | SK = READING#2026-06-23T08:05Z |
| PK = DEVICE#a91 | SK = READING#2026-06-23T08:10Z |
B-tree はソートされているので、「08:00 から 09:00 までのすべての測定値」は、開始値までの木の下降に加えて逐次的な 歩みです — そのデバイスがこれまでに送ったすべての測定値に対するフィルターではありません。一致する範囲だけを 読み取ります。
その順序付けは、begins_with(SK, "READING#2026-06-23") クエリが高速で、非キー属性でのフィルタリングが
そうでない理由でもあります。木は SK でシークできますが、それ以外では何もシークできません。それらのキー
条件を安全に組み立てるには、文字列を手で連結するのではなく、
DynamoDB 式ビルダー で構築しましょう。
KeyConditionExpression PK = :pk AND begins_with(SK, :day)
すべての書き込みを3つの AZ に複製する
パーティションは1台のマシンではありません。それぞれは3つのアベイラビリティーゾーンの3つのノードに 複製されます — 2022年の USENIX ATC の DynamoDB 論文で詳述された、リーダーベースのクォーラム設計です (2007年の Amazon Dynamo 論文は、命名と哲学の祖先であって、このレプリケーションモデルではありません)。
1つのノードがそのパーティションのリーダーです。書き込みはリーダーへ向かい、リーダーはローカルに書き込んで
2つのピアに複製します。リーダーは、耐久性のあるクォーラムのノードがそれを持った時点で書き込みを確認応答します —
なので、AZ にまたがる耐久性は PutItem が返る 前 に支払われます。
読み取りには選択肢があります。 読み取りはリーダーへ向かい、最新の コミット済みの書き込みを見ます。 読み取りは、3つのノードのいずれか — そのうち1つは数ミリ秒遅れているかもしれません — によって提供されます。それが、より安価でより可用性の高い 読み取りと引き換えにするラグです。
| 結果整合性 | 強い整合性 | |
|---|---|---|
| 提供元 | 3つのノードのいずれか | リーダーノードのみ |
| 最新の書き込みを見る | 場合による(小さなラグ) | 常に |
| RCU コスト | 半分 | 満額 |
| 可用性 | より高い | より低い(単一ノード) |
オンデマンド課金では、2 KB のアイテムを強い整合性で読むと 1 RCU、同じ読み取りを 結果整合性で行うと 0.5 RCU です。ホットパスの単価は 料金計算ツールで確かめましょう。
同じ非同期伝播の考え方が、GSI の読み取りが古くなりうる理由です — GSI は結果整合性であるを参照してください。
構造からルールを読み取る
DynamoDB の「ルール」のほとんどは、ただのストレージの物理です。
- 常にパーティションキーを供給する。 キーがなければハッシュのターゲットもありません — マップ全体を スキャンしています。これが Query 対 Scan の核心です。
- 一緒に読むものを1つのパーティションキーの下に共置する。 そうすれば、単一のハッシュ + 木の歩みで アイテムコレクション全体が返ります。それが シングルテーブル設計 の基盤です。
- パーティションを有界に保つ。 1つのパーティションは、有限のノード集合上の1本の B-tree です。暴走した ホットキーも LSI の 10 GB 上限も、どちらもその物理パーティションの限界です。
ハッシュしてから B-tree、という形が見えると、アクセスパターンの規律は恣意的に感じられなくなります — あなたはただ、すべての読み取りを高速な経路に保っているだけです。
次のステップ
ソートキー戦略とシングルテーブル設計で キーを構造に合わせてモデリングし、それから DynamoDB 式ビルダーで 実際の式を組み立てましょう。DynoTable を試して これらの読み取りが自分自身のテーブルに対して 走る様子を眺め、キー条件がどのアイテムを引き戻すかを正確に見てください。