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

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