| name | dynamodb-designer |
| description | DynamoDB テーブルを設計し、NoSQLのよくある落とし穴を避けながら効率的なクエリを書くスキル。テーブル設計・インデックス戦略・クエリ最適化・トランザクション・キャパシティ管理に対応。「DynamoDBのテーブルを設計して」「GSIを追加して」「クエリを最適化して」などで発動。 |
| metadata | {"version":"1.0.0","tier":"experimental","category":"data","tags":["dynamodb","nosql","aws"]} |
トリガー
| トリガーワード | 例 |
|---|
| テーブル設計 | 「DynamoDB のテーブルを設計して」「テーブル構造を考えて」 |
| インデックス | 「GSI を追加して」「LSI が必要か教えて」「インデックス設計して」 |
| クエリ相談 | 「このクエリを最適化して」「Scan を避けるには」「クエリを書いて」 |
| キャパシティ | 「RCU/WCU を見積もって」「オンデマンドとプロビジョンドどちらが良い?」 |
| TTL / 有効期限 | 「TTL を設定したい」「期限切れデータを自動削除したい」 |
| トランザクション | 「アトミックに書き込みたい」「複数テーブルを同時更新したい」 |
出力フォーマット
依頼の種類に応じて以下の形式で回答する:
テーブル設計の依頼:
## テーブル設計: <テーブル名>
### ⚠️ シングルテーブル設計の適否
- 適している場合: (アクセスパターンが明確・エンティティ間に親子関係がある等)
- 適さない場合: (アクセスパターンが多様すぎる・チームの習熟度が低い等)
- 採用判断: シングルテーブル / マルチテーブル
### キー構造
| 属性 | 役割 | 型 | 例 |
|------|------|-----|-----|
| pk | パーティションキー | String | USER#123 |
| sk | ソートキー | String | ORDER#2024-01-15 |
### アクセスパターン
| パターン | 操作 | キー条件 | インデックス |
|----------|------|----------|--------------|
| ユーザーの注文一覧 | Query | pk=USER#123 | メインテーブル |
### GSI(必要な場合)
| インデックス名 | パーティションキー | ソートキー | 用途 | 整合性 |
|----------------|-------------------|------------|------|--------|
### LSI(必要な場合)
| インデックス名 | ソートキー | 用途 | 注意: テーブル作成時のみ追加可 |
|----------------|------------|------|-------------------------------|
### 設計上の注意点
- (ホットパーティション・キャパシティ・制限値等の落とし穴を箇条書き)
クエリ相談の依頼:
## クエリ設計
### 推奨操作: Query / Scan / BatchGet
- キー条件: <具体的な条件式>
- フィルター: <FilterExpression があれば(読み取り後適用の注意を添える)>
- ページネーション: <1MB 超の場合は LastEvaluatedKey ループが必要か>
- 整合性: <結果整合性 or ConsistentRead: true が必要か>
### ⚠️ 避けるべきアンチパターン
- (Scan を使いそうなケースへの GSI 代替案等)
キャパシティ見積もりの依頼:
## キャパシティ見積もり
### モード推奨: オンデマンド / プロビジョンド
- 理由: (トラフィック予測可否・コスト観点)
### プロビジョンド選択時の目安
| 項目 | 計算根拠 | 推奨値 |
|------|----------|--------|
| RCU | リクエスト数 × アイテムサイズ(4KB単位) | |
| WCU | 書き込み数 × アイテムサイズ(1KB単位) | |
### 注意点
- (GSI の独立キャパシティ・スロットリングリスク等)
トランザクション相談の依頼:
## トランザクション設計
### 操作種別: TransactWriteItems / TransactGetItems
- 理由: (書き込みのみ → TransactWriteItems、複数アイテムの一貫読み取り → TransactGetItems)
### 操作リスト
| # | テーブル | 操作 | 条件 |
|---|----------|------|------|
| 1 | <テーブル名> | Put / Update / Delete / ConditionCheck | <ConditionExpression があれば> |
### コスト
- 通常操作の 2x — アトミック性が本当に必要か確認する
### ⚠️ 注意点
- 上限: 最大 100 アイテム・合計 4MB
- TransactionConflictException = 同一アイテムへの同時トランザクション—リトライ設計が必要
- (代替案: ConditionExpression の単独書き込みで済む場合はそちらを推奨)
TTL 設計の依頼:
## TTL 設計
- 属性名: <任意の属性名(例: ttl, expires_at)>
- 型: Number(Unix エポック秒—ミリ秒不可)
- 削除タイミング: バックグラウンド処理のため期限切れ後数時間残存する可能性あり
- クエリでの除外: `attribute_exists(ttl) AND ttl > :now` を FilterExpression に追加
キー設計
- パーティションキーはデータ分散を決定する—高カーディナリティのキーが負荷を均等に分散させる
- ホットパーティション = 1つのキーにトラフィックが集中—複合キーかランダムサフィックスを使用する
- ソートキーはパーティション内の範囲クエリを可能にする—アクセスパターンに合わせて設計する
- キーは作成後に変更不可—テーブル作成前にすべてのアクセスパターンをモデル化する
Query vs Scan
- Query はパーティションキー + オプションのソートキーを使用—O(パーティション内のアイテム数)、常にこちらを優先
- Scan はテーブル全体を読み取る—高コスト・低速・インデックス非使用、ほぼ常に誤り
- 「X でフィルタしたい」は大抵 GSI が足りていない—インデックスを追加、Scan はしない
- FilterExpression は読み取り後に適用—フルの読み取りキャパシティを消費する
グローバルセカンダリインデックス(GSI)
- GSI = 別のパーティション/ソートキー—代替アクセスパターンを実現する
- GSI は結果整合性—書き込みはわずかな遅延を伴って伝播する
- GSI は独立したキャパシティを消費—各 GSI に対して個別にプロビジョニングまたは課金される
- スパースインデックスのテクニック:属性を持つアイテムのみが GSI に現れる
ローカルセカンダリインデックス(LSI)
- LSI = 同じパーティションキーで別のソートキー—テーブル作成時のみ追加可能(後から変更不可)
- LSI は強整合性読み取りが可能—GSI と異なり
ConsistentRead: true が使える
- LSI はテーブルのキャパシティを共有—独立したプロビジョニング不要
- パーティションあたりのデータが 10GB を超えると LSI のあるパーティションは書き込み不可になる
- GSI vs LSI の選択:強整合性が必要 → LSI、テーブル作成後に追加が必要 → GSI
シングルテーブル設計
- 複数エンティティタイプを1テーブルに—パーティションキーにプレフィックス:
USER#123、ORDER#456
- ソートキーのオーバーロード:
METADATA、ORDER#2024-01-15、ITEM#abc
- Query は混在型を返す—クライアント側でフィルタするか
begins_with を使用
- 常に正解とは限らない—ドクトリンではなくアクセスパターンから始める
ページネーション
- 1リクエストあたりの結果は 1MB に制限—ページネーション処理が必須
- レスポンスに
LastEvaluatedKey があれば続きがある—ExclusiveStartKey として渡す
LastEvaluatedKey が空になるまでループ—よくある間違い:1回の呼び出しで全件取得できると思い込む
Limit は評価するアイテム数を制限(返却数ではない)—それでもページネーション処理が必要
整合性
- デフォルトの読み取りは結果整合性—古いデータが返る場合がある
ConsistentRead: true で強整合性—読み取りキャパシティが 2x となる
- GSI 読み取りは常に結果整合性—強整合性オプションなし
- 書き込み後の読み取りには強整合性読み取りかリトライが必要—結果整合性の落とし穴
条件付き書き込み
- 楽観的ロックには
ConditionExpression—条件が偽なら失敗する
- 上書き防止:
attribute_not_exists(pk)
- バージョンチェック:
version = :expected の後にインクリメントする
- ConditionCheckFailedException = 新鮮なデータで再試行、単純に失敗させない
バッチ操作
BatchWriteItem はアトミックではない—部分成功あり、UnprocessedItems を確認する
- 未処理分はエクスポネンシャルバックオフでリトライ—AWS SDK に組み込み済み
- 1バッチあたり最大 25 アイテム、合計 16MB—大きなバッチは分割する
- バッチでは条件付き書き込み不可—アトミック性が必要なら
TransactWriteItems を使用
トランザクション
TransactWriteItems でアトミックなマルチアイテム書き込み—all or nothing
- 1トランザクションあたり最大 100 アイテム、合計 4MB
TransactGetItems で整合性のあるマルチ読み取り—スナップショット分離
- 通常操作の 2x コスト—アトミック性が必要な場合のみ使用
TTL
- タイムスタンプ属性に TTL を有効化—DynamoDB が期限切れアイテムを自動削除する
- 削除はバックグラウンドプロセス—期限切れ後も数時間残る場合がある
- TTL の値は Unix エポック秒—ミリ秒は無言で失敗する
- 必要に応じてクエリに
attribute_exists(ttl) AND ttl > :now でフィルタ
キャパシティ
- オンデマンド: リクエストごとの課金、自動スケール—予測不可能なトラフィックに最適
- プロビジョンド: RCU/WCU を設定、大規模では安価—キャパシティプランニングが必要
- 予測可能なパターンにはオートスケーリング付きプロビジョンド—min/max/target を設定
- ProvisionedThroughputExceededException = スロットリング—バックオフしてリトライ
制限値
- アイテムサイズ最大 400KB—大きなオブジェクトは S3 に保存し DynamoDB で参照
- パーティションスループット: 3000 RCU、1000 WCU—パーティション間に分散させる
- Query/Scan は最大 1MB を返す—それ以上はページネーションが必要
- アイテムごとの属性名合計 64KB—長い属性名は使わない