Amazon DynamoDBとは何か、そのNoSQLモデルがどう機能するか、シングルテーブル設計やパーティション/ソートキー、インデックス、容量モード、Streamsを使ったイベント駆動パターンなど、スケーラブルで低レイテンシなシステム設計の実践を解説します。

Amazon DynamoDBはAWSのフルマネージドなNoSQLデータベースサービスで、ほぼどんな規模でも一貫して低レイテンシな読み書きを必要とするアプリケーション向けに設計されています。「フルマネージド」とは、ハードウェアのプロビジョニング、レプリケーション、パッチ適用や多くの運用作業をAWSが扱うという意味で、チームはデータベースサーバの運用ではなく機能の提供に集中できます。
コアでは、DynamoDBはテーブル内のアイテム(行)としてデータを保持しますが、各アイテムは柔軟な属性を持てます。データモデルは次の混合と考えると理解しやすいです。
チームがDynamoDBを選ぶのは、一貫した性能と運用の簡潔さを得たい場合、特にリレーショナルな結合に収まりきらないワークロードで多いです。マイクロサービス(各サービスが自分のデータを所有)、バーストするトラフィックのサーバレスアプリ、データ変更に反応するイベント駆動システムでよく使われます。
この記事では、ビルディングブロック(テーブル、キー、インデックス)、アクセスパターンに沿ったモデリング(シングルテーブル設計を含む)、スケーリングと容量モードの仕組み、そして変更をイベント駆動アーキテクチャに流す実践的パターンを説明します。
DynamoDBは少数のシンプルな構成要素を中心に組織されていますが、その詳細はデータのモデリングやリクエストの速度(とコスト)に直結します。
テーブルはトップレベルのコンテナです。テーブル内の各レコードはアイテム(行に相当)で、各アイテムは属性(列に相当)の集合です。
リレーショナルデータベースと異なり、同じテーブル内のアイテムは同じ属性群を共有する必要はありません。あるアイテムは {status, total, customerId} を持ち、別のアイテムは {status, shipmentTracking} を持つことができます—DynamoDBは固定スキーマを要求しません。
すべてのアイテムは主キーで一意に識別されます。DynamoDBは二つのタイプをサポートします:
実務では、複合キーは「顧客の全注文を最新順で取得する」といった“グループ化”されたアクセスパターンを可能にします。
Queryは主キー(またはインデックスキー)でアイテムを読みます。特定のパーティションキーをターゲットにし、ソートキー範囲でフィルタできる—これが効率的で推奨される経路です。
Scanはテーブル(またはインデックス)全体を走査してからフィルタします。始めるのは簡単ですが、スケールしたときには遅くコストが高くなることが多いです。
最初に感じるであろういくつかの制約:
これらの基本が、以降のアクセスパターン、インデックス選択、性能特性を決めます。
DynamoDBはしばしばキー・バリュー型とドキュメント型の両方と説明されます。どちらも正しいのですが、それぞれが設計に与える含意を日々の設計で理解しておくと役立ちます。
核心はキーでデータを取得することです。主キーの値を与えればDynamoDBは単一のアイテムを返します。キーによるルックアップが多くのワークロードで予測可能で低レイテンシなストレージを実現します。
同時に、アイテムはネストした属性(マップやリスト)を含められるため、ドキュメントデータベースのように振る舞います。厳密なスキーマを事前定義せず構造化ペイロードを保存できます。
アイテムはJSONライクなデータに自然にマッピングします:
profile.name, profile.address)を表すユーザープロファイル、ショッピングカート、設定の束など、エンティティ全体を一度に読むことが多い場合に適しています。
DynamoDBはサーバーサイドでの結合をサポートしません。アプリが「注文とその明細と配送状況」を1回の読み取りで取得したい場合、しばしば**非正規化(denormalize)**します:いくつかの属性を複数のアイテムにコピーするか、小さなサブ構造をアイテム内に埋め込みます。
非正規化は書き込みの複雑さを増し、更新の扇状拡散を生む可能性があります。見返りはラウンドトリップの削減と高速な読み取りであり、スケーラブルなシステムでは多くの場合これが重要です。
最速のDynamoDBクエリは「このパーティションをくれ(必要ならソートキーの範囲も)」で表現できるものです。だからキーの選択は単に保存方法ではなく、どう読みたいかに関するものです。
パーティションキーはアイテムをどの物理パーティションに配置するかを決めます。DynamoDBはこの値をハッシュしてデータとトラフィックを分散します。少数のパーティションキー値にリクエストが集中すると「ホット」パーティションができ、テーブル全体がアイドルでもスループット制限に達することがあります。
良いパーティションキーの特徴:
"GLOBAL"のような値)ソートキーを使うと、同じパーティションキーを共有するアイテムは一緒に保存され、ソートキー順に並びます。これにより効率的な:
BETWEEN, begins_with)よくあるパターンはソートキーを合成することです。たとえば TYPE#id や TS#2025-12-22T10:00:00Z のようにして、追加のテーブルを作らずに複数のクエリ形状をサポートします。
PK = USER#<id>(単純な GetItem)PK = USER#<id>, SK begins_with ORDER#(または SK = CREATED_AT#...)PK = DEVICE#<id>, SK = TS#<timestamp> を BETWEEN で検索パーティションキーが最も高頻度のクエリに合致し、かつ均等に分散するなら、読み書きは一貫して低レイテンシになります。合致しない場合、Scanやフィルタ、追加のインデックスで補おうとし、それぞれコストやホットキーのリスクを高めます。
セカンダリインデックスはベーステーブルの別のクエリ経路を提供します。新しいアクセスパターンが現れたときにテーブル設計を根本的に変える代わりに、インデックスで同じアイテムを別キーで参照できます。
**グローバルセカンダリインデックス(GSI)**は独自のパーティションキー(とオプションのソートキー)を持ち、ベーステーブルとまったく異なるキー設計が可能です。テーブル全体にまたがり、後から追加や削除ができます。例えば、テーブルが orderId でキー付けされているときに customerId で検索したければGSIを使います。
**ローカルセカンダリインデックス(LSI)**はベーステーブルと同じパーティションキーを共有し、異なるソートキーを使います。LSIはテーブル作成時に定義する必要があり、同一のエンティティグループ内で複数のソート順を持ちたいときに有用です(例:顧客の注文を createdAt と status の両方で取り出したい場合)。
プロジェクションはインデックスに保存される属性を決めます:
ベーステーブルへの各書き込みは1つ以上のインデックスへの書き込みを引き起こします。GSIを増やし、プロジェクションを広くすると書き込みコストと容量消費が増えるので、安定したアクセスパターンに基づいてインデックスを計画し、プロジェクションは必要最小限に留めてください。
DynamoDBのスケーリングはオンデマンドかプロビジョンドの選択から始まります。どちらも高スループットに到達できますが、変動するトラフィックに対する挙動が異なります。
オンデマンドは最も簡単です:リクエストごとに課金され、DynamoDBが可変負荷を自動で吸収します。予測できないトラフィックやアーリー段階、スパイクするワークロードに向きます。
プロビジョンドはキャパシティ設計です:読み書きのスループットを指定(またはオートスケールで調整)し、安定した使用時により予測可能な料金を得られます。使用が安定している場合や需要予測ができるチームに向きます。
プロビジョンドスループットは:
アイテムサイズや整合性、Scanの有無が実際のコストを大きく左右します。大きなアイテムや強整合、Scanは容量を急速に消費します。
オートスケーリングは利用率目標に基づいてRCU/WCUを調整します。緩やかな成長や予測可能な周期には有効ですが、即時対応はできません。急激なスパイクではキャパシティが追いつかずスロットリングする可能性があり、ホットパーティションの問題は解決できません。
**DynamoDB Accelerator(DAX)**はインメモリキャッシュで、繰り返しの読み取りをオフロードしレイテンシを削減します(人気の商品ページやセッション参照など)。書き込み重視のパターンには効果がなく、キー設計の代替にはなりません。
DynamoDBは読み取り保証とレイテンシ/コストのトレードオフを許容するため、各操作で「正しさ」が何を意味するかを明確にすることが重要です。
デフォルトの GetItem や Query は最終的整合性を使います。書き込み直後に古い値が返る可能性があります。これはフィードや商品カタログなど多くの読み取りが許容するシナリオに十分です。
強整合性を使うと、そのリージョン内のベーステーブルに対して最新の確認済み書き込みが必ず見える保証があります。強整合性は読み取り容量を多く消費しテールレイテンシを増やすため、本当に重要な読み取りに限定してください。
カウンタの扱いは通常、「強い読み取りしてから書き込む」ではなく、UpdateItem の ADD のような原子的更新が安全です。
DynamoDBトランザクション(TransactWriteItems, TransactGetItems)は最大25アイテムにわたるACIDを提供します。複数アイテムを同時に更新する必要があるフロー(例:注文作成と在庫予約)や、中間状態を許容できない不変条件の強制に有効です。
分散システムでは再試行は普通です。再試行で重複した効果が発生しないように書き込みを冪等化してください:
ConditionExpression(例:attribute_not_exists)で一意性を強制するDynamoDBにおける正しさは、適切な整合性レベルの選択と再試行でデータが壊れない操作設計に尽きます。
DynamoDBはテーブルデータを複数の物理パーティションに分散して保存します。各パーティションには読み書きのスループットと保持可能なデータ量の制限があります。あるアイテムがどのパーティションにあるかはパーティションキーで決まるため、特定のキー値(または少数のキー値)に大量のリクエストが集中するとそのパーティションがボトルネックになります。
ホットパーティションは通常、キー選択がトラフィックを集中させるときに発生します:USER#1、TENANT#default、STATUS#OPEN のような“グローバル”パーティション、または時間順で誰もが「今」を同じキーで書き込むパターンなどです。
典型的には:
ProvisionedThroughputExceededException)まずは分散に配慮してからクエリの利便性を考える:
TENANT#<id>)ORDER#<id>#<shard> のように小さなランダム/ハッシュ接尾辞をつけて書き込みをNシャードに分散し、必要時にシャード横断で集約するMETRIC#2025-12-22T10)して「最新だけに書き込まれる」状況を避ける予測できないスパイクにはオンデマンドが吸収できます(サービス上限内)。プロビジョンドではオートスケールとクライアント側の指数バックオフ+ジッターを併用して、スロットリング時の同調再試行でスパイクを悪化させないようにします。
DynamoDBのデータモデリングはER図からではなく、アクセスパターンから始めます。必要なクエリが Query 操作で高速に表現できるようにキーを設計し、それ以外の処理は非同期で扱うのが基本です。
「シングルテーブル設計」は複数のエンティティタイプ(ユーザー、注文、メッセージ)を1つのテーブルに保存し、一貫したキー規約で関連データを1つの Query で取得できるようにする考え方です。これによりエンティティ間のラウンドトリップが減り、レイテンシが予測可能になります。
よくあるアプローチは複合キーを使うことです:
PK は論理的なグループ(例:USER#123)SK はそのグループ内のアイテムを順序づける(例:PROFILE, ORDER#2025-12-01, MSG#000123)これで「ユーザーの全て」や「そのユーザーの注文のみ」をソートキーのプレフィックスで取得できます。
グラフ的な関係は隣接リストで表現するのが有効です:辺をアイテムとして保存します。
PK = USER#123, SK = FOLLOWS#USER#456逆方向のルックアップや真の多対多をサポートするには、逆向きのエッジアイテムを追加するか、読み取りパスに応じてGSIへプロジェクトします。
イベントやメトリクスでは無限に大きくなるパーティションを避けるためにバケット化します:
PK = DEVICE#9#2025-12-22(デバイス+日)SK = TS#1734825600(タイムスタンプ)TTLで古いポイントを自動削除し、ダッシュボード向けに時間ごとの集計(hourly/daily rollups)を別アイテムとして保存しておくと高速に集計できます。
キー規約の詳しいリフレッシャーは /blog/partition-key-and-sort-key-design を参照してください。
DynamoDB Streamsは組み込みの変更データキャプチャ(CDC)フィードです。テーブルで有効にすると、挿入・更新・削除のたびにストリームレコードが生成され、下流のコンシューマがポーリングなしで反応できます。
ストリームレコードはキーと(選択したストリームビュータイプに応じて)アイテムの旧イメージ/新イメージを含みます。レコードはシャードにグループ化され、順次読み取ります。
一般的な構成は DynamoDB Streams → AWS Lambda で、レコードのバッチごとに関数が呼ばれます。他のコンシューマ(カスタムコンシューマや分析/ログ系へのパイプ)も可能です。
典型的なワークフロー:
これにより、プライマリテーブルは低レイテンシの読み書きに最適化しつつ、派生的な作業を非同期に押し出せます。
Streamsはシャードごとの順序を提供しますが(通常パーティションキーに相関します)、すべてのキーに対するグローバル順序はありません。配信はat-least-onceなので重複が発生する可能性があります。
安全に処理するために:
これらの保証を考慮すれば、StreamsはDynamoDBをイベント駆動システムの堅牢な基盤に変えられます。
DynamoDBは複数のアベイラビリティゾーンにデータを分散して高可用性を実現するよう設計されています。実務での信頼性向上は、明確なバックアップ戦略、レプリケーションオプションの理解、適切なメトリクス監視から得られます。
オンデマンドバックアップはスナップショットを手動(または自動化)で取得する方法で、マイグレーション前やリリース後、大規模なバッチ処理の前などに使います。
**PITR(Point-In-Time Recovery)**は変更を継続的にキャプチャし、保持ウィンドウ内の任意の秒にテーブルを復元できます。誤削除や不正な書き込みに対する安全網になります。
マルチリージョンの耐障害性やユーザー近傍の低レイテンシを求めるなら、Global Tablesで選択したリージョンにデータを複製できます。フェイルオーバー計画が簡素化されますが、リージョン間複製遅延や競合解決の考慮が必要です。書き込みパターンとアイテム所有権を明確に保ってください。
最低でも次をアラート対象にしてください:
これらの指標は通常、ホットパーティション問題、不十分なキャパシティ、または予期しないアクセスパターンを示します。
スロットリングが起きたら、まず原因となるアクセスパターンを特定し、一時的にオンデマンドに切り替えるかプロビジョンドを増やし、ホットキーならシャーディングを検討します。
部分的な障害やエラー増加時は、被害範囲を縮小するために非クリティカルなトラフィックを止め、ジッター付きバックオフで再試行し、テーブルが安定するまでキャッシュで応答させるなどフェイルグレースする方針を取ります。
DynamoDBのセキュリティは主に「誰がどのAPIアクションをどこからどのキーに対して実行できるか」を絞ることです。テーブルに多くのエンティティタイプ(あるいは複数のテナント)を置く場合、アクセス制御はデータモデルと並行して設計すべきです。
まずはアイデンティティベースのIAMポリシーでアクション(例:dynamodb:GetItem, Query, PutItem)を最小限にし、特定のテーブルARNにスコープしてください。
より細かい制御が必要なら dynamodb:LeadingKeys を使ってパーティションキー値でアクセスを制限できます。サービスやテナントが自分のキー空間だけにアクセスすべき場合に有用です。
DynamoDBはデフォルトで保存時暗号化を行います(AWS管理キーまたはカスタマー管理KMSキー)。コンプライアンス要件がある場合は:
通信路の暗号化(in transit)についてはクライアントがHTTPSを使っていることを確認してください(AWS SDKはデフォルトでHTTPS)。プロキシでTLS終端する場合、プロキシとDynamoDB間の通信も暗号化されているか確認してください。
VPC Gateway Endpointを使えばトラフィックがAWSネットワーク内に留まり、エンドポイントポリシーでアクセス制御が可能です。これにNACLやセキュリティグループ、ルーティングでの出口制御を組み合わせ、無制限にインターネットへ出られる経路を避けてください。
共有テーブルの場合、パーティションキーにテナント識別子を含め(例:TENANT#<id>)、dynamodb:LeadingKeys でテナント分離を強制します。
より強い分離が必要なら、テナントごとにテーブルを分けるか環境ごとにテーブルを分けることを検討してください。運用の簡便さやコスト効率が分離より重要な場合のみ共有テーブルを採用します。
DynamoDBは「正確に設計すれば安価だが、曖昧だと高価になる」ことが多いです。コストはアクセスパターンに従うため、最良の最適化作業はまずアクセスパターンを明確にすることから始まります。
請求は主に以下で決まります:
一般的な驚きどころ:ベーステーブルへの1回の書き込みは影響を受けるすべてのGSIへの書き込みにもなります。つまり「インデックスを一つ増やすだけ」で書き込みコストが倍増することがあり得ます。
良いキー設計は無駄な操作を減らします。頻繁に Scan を行っているなら、読み捨てるデータを読み込むためのコストを払っていることになります。
推奨:
稀なアクセスパターンは、別テーブルやETL、キャッシュ済みのリードモデルで提供することを検討してください。
TTLを使ってセッション、短命トークン、中間ワークフローステートのような短命アイテムを自動削除するとストレージを削減できます。
イベントやログのような追加型データにはTTLと、最近のみを対象にするソートキー設計を組み合わせて、コールドな履歴に日常的に触れないようにします。
プロビジョンドモードでは保守的なベースラインを設定してオートスケールで拡大してください。オンデマンドでは非効率なパターン(大きなアイテム、チャッティなクライアント)がリクエスト量を押し上げないか監視します。
Scanは最後の手段として扱い、全表処理が必要な場合はオフピークでページネーションとバックオフを伴う制御されたバッチとして実行してください。
DynamoDBはトップクエリが明確に定義でき、低レイテンシを高スケールで維持したい場合に非常に適しています。主要な読み書きを前もって(パーティションキー、ソートキー、少数のインデックスで)表現できるなら、運用の容易さから優れた選択肢になることが多いです。
DynamoDBは次のような場合に強みを発揮します:
以下がコア要件にあるなら他を検討してください:
多くのチームはホットな運用読み書きにDynamoDBを使い、次を追加します:
アクセスパターンやシングルテーブルの規約を検証するにはスピードが重要です。チームはしばしば Koder.ai のようなプロトタイピングプラットフォームで周辺サービスとUIを早く作り、実際のクエリパスが出てきた段階でDynamoDBのキー設計を反復します。運用バックエンドが本番と異なっても、エンドツーエンドのプロトタイプはどのクエリが Query で済むべきか、どれが誤って高価な Scan になるかを明らかにします。
検証すること: (1) トップのクエリがキーに基づいて既知であるか、(2) 正しさ要件が整合性モデルと合致するか、(3) 予想されるアイテムサイズと成長が理解されているか、(4) コストモデル(オンデマンド vs プロビジョンド+オートスケール)が予算に合うか。
DynamoDBは、非常に大規模でも一貫して低レイテンシな読み書きを提供するAWSのフルマネージドなNoSQLデータベースです。チームは、キーに基づくアクセスパターン(IDで取得、オーナー別の一覧、時間範囲検索など)を定義でき、データベースのインフラ管理を避けたい場合にDynamoDBを選びます。
特にマイクロサービス、サーバーレスアプリ、イベント駆動システムでよく使われます。
テーブルはアイテム(行のようなもの)を保持します。各アイテムは、ネスト可能なデータを含められる柔軟な属性(列のようなもの)で構成されます。
DynamoDBは「1回のリクエストでエンティティ全体を取得する」パターンに向いています。アイテムはmapsやlistsのようなJSON的な構造を含められます。
単一のパーティションキーだけでアイテムを一意に識別するのが単純プライマリキーです。パーティションキー+ソートキーの複合キーは、同じパーティションキーを共有する複数アイテムを区別し、ソートキーによって順序付けできます。
複合キーは例えば:
といったパターンを可能にします。
Queryはパーティションキー(とオプションでソートキー条件)を指定して実行します。高速でスケーラブルな経路です。
Scanはテーブル(またはインデックス)全体を走査してからフィルタを適用します。通常は遅くコストが高いので、頻繁に使うべきではありません。
もし頻繁にScanしているなら、キーやインデックス設計の見直しが必要です。
セカンダリインデックスは別のクエリ経路を提供します。
インデックスは書き込み時に複製が発生するため、書き込みコストが増える点に注意してください。
トラフィックが予測できずスパイクがある場合や容量管理を避けたい場合はオンデマンドが適しています。リクエストごとに課金され、自動で負荷を吸収します。
使用量が安定して予測可能で、よりコスト効率を追求する場合はプロビジョンド(Auto Scalingと組み合わせて)を選びます。ただし急激なスパイクには即時対応できないことがあります。
デフォルトは**最終的整合性(eventually consistent)**です。直後の書き込み後に古い値を読む可能性があります。
**強い整合性(strongly consistent)**を選ぶと直近の書き込みを必ず見る保証がありますが、追加の読み取り容量を消費し、テールレイテンシが増える可能性があります。
競合下での正しさが必要なら、読み取り→書き込みのループではなく、ADDや条件付き更新などの原子的更新を優先してください。
トランザクション(TransactWriteItems, TransactGetItems)は最大25アイテムに対してACIDを提供します。
複数アイテムを一緒に更新する必要がある(例:注文作成と在庫確保)か、途中状態を許容しない不変条件を保つ必要がある場合に使います。コストとレイテンシが増すので、本当に必要なフローに限定してください。
ホットパーティションは、特定のパーティションキーにリクエストが集中してスロットリングが発生する現象です。テーブル全体がアイドルでも一部のキーだけが過負荷になります。
一般的な対策:
DynamoDB Streamsを有効にすると、挿入・更新・削除の変更フィードが得られます。一般的な構成は DynamoDB Streams → Lambda で、バッチごとに関数がトリガーされ下流処理を行います。
重要な特性:
消費者側は冪等化(キーでupsertする、条件付き書き込みにする、処理済みイベントIDを記録する等)を行ってください。