💻

DynamoDBマスターガイド 〜俺でも分かるAWSのNoSQL〜

2025/03/24に公開
1

0. このカンニングペーパーの使い方

このカンニングペーパーではDynamoDBの基本から実践的な使い方まで、コンパクトにまとめています。困った時に参照してください。専門用語には太字で印をつけてあります。

1. DynamoDBの基本:そもそも何者?

1.1 DynamoDBとは?

  • AWSが提供するNoSQLデータベース
  • 従来のRDB(MySQL, PostgreSQLなど)と違い、テーブル間の関連性より「速さ」と「スケーラビリティ」を重視
  • フルマネージド型(運用の手間が少ない)
  • ミリ秒単位の応答速度が特徴

1.2 RDBとの主な違い

項目 RDB (例:RDS) DynamoDB
データモデル 正規化されたテーブル、固定スキーマ スキーマレス、柔軟な構造
クエリ言語 SQL 専用API (SQLなし)
スケーリング 垂直スケーリング(サーバースペックアップ) 水平スケーリング(分散自動化)
設計アプローチ スキーマ先行設計 アクセスパターン先行設計
得意分野 複雑なJOIN、複雑なクエリ シンプルな読み書きの高速処理

1.3 基本的な構造

  • テーブル:データの箱。例えば「ユーザーテーブル」「商品テーブル」。
  • アイテム:テーブル内の1レコード。例えば「ユーザーテーブル内の田中さんのデータ」。
  • 属性:アイテムが持つ情報。例えば「名前」「年齢」「メールアドレス」。
// ユーザーテーブルのアイテム例
{
  "user_id": "123",
  "name": "田中太郎",
  "email": "tanaka@example.com",
  "age": 28,
  "preferences": {
    "theme": "dark",
    "notifications": true
  },
  "last_login": "2025-03-20T10:30:00Z"
}

2. キホンのキ:キーの理解

2.1 キーの種類

  • プライマリキー:必須。以下の2種類がある
    • パーティションキーのみ:単一キー構成
    • パーティションキー + ソートキー:複合キー構成

2.2 パーティションキーとは?

  • データがどの区画(パーティション)に保存されるかを決める値
  • 例えば「user_id」「product_id」など
  • 均等に分散する値を選ぶとパフォーマンスが向上
  • 👉 ホットパーティション問題:特定のパーティションにアクセスが集中すると性能低下

2.3 ソートキーとは?

  • 同じパーティション内でのデータの並び順を決める値
  • 例えば「登録日」「注文ID」など
  • 範囲検索(○○以上××以下)も可能
  • パーティションキーとセットで一意になるように設計
パーティションキー:user_id = "123"
ソートキー:timestamp = "2025-03-01T..." 〜 "2025-03-31T..."
→ 3月の全注文履歴が取得できる

3. データの取り出し方

3.1 クエリ操作

  • 特定のパーティションキーに対してデータを取得
  • 効率が良く、高速
  • ソートキーがある場合は、その範囲や条件での絞り込みも可能
// JavaScriptでの例
const params = {
  TableName: "Users",
  KeyConditionExpression: "user_id = :uid",
  ExpressionAttributeValues: {
    ":uid": "123"
  }
};

await dynamodb.query(params).promise();

3.2 スキャン操作

  • テーブル全体を一つずつ確認する方法
  • データが多いとめっちゃ時間かかるしコストもかかる
  • できるだけ避けるべき操作
// JavaScriptでの例 (非推奨)
const params = {
  TableName: "Users",
  FilterExpression: "age > :min_age",
  ExpressionAttributeValues: {
    ":min_age": 20
  }
};

await dynamodb.scan(params).promise();

4. インデックスで検索を効率化

4.1 グローバルセカンダリインデックス (GSI)

  • パーティションキーとは別の属性でデータを探せるようにする
  • 例:ユーザーIDではなく、メールアドレスからユーザーを探したい
  • テーブル作成後でも追加・変更・削除可能
// メールアドレスからユーザーを検索する例
const params = {
  TableName: "Users",
  IndexName: "email-index",
  KeyConditionExpression: "email = :email",
  ExpressionAttributeValues: {
    ":email": "tanaka@example.com"
  }
};

await dynamodb.query(params).promise();

4.2 ローカルセカンダリインデックス (LSI)

  • 同じパーティションキーだけど、別のソートキーでデータを探せるようにする
  • テーブル作成時にしか設定できない
  • 使用頻度はGSIより少ない

5. キャパシティ設定:DynamoDBの処理能力

5.1 プロビジョンドモード

  • 1秒間に何回読み書きできるかを事前に設定
  • RCU(読み取りキャパシティユニット)とWCU(書き込みキャパシティユニット)で設定
  • 予測可能なトラフィックに最適
  • 自動スケーリングと組み合わせて使うのが一般的

5.2 オンデマンドモード

  • 使った分だけ支払う方式
  • 急なアクセス増加にも自動対応
  • トラフィック予測が難しい場合に便利
  • ただし、長期的にはプロビジョンドより高くなりがち

5.3 💰 コスト比較ヒント

  • スタートアップフェーズ → オンデマンド(柔軟性重視)
  • 安定フェーズ → プロビジョンド + 自動スケーリング(コスト最適化)
  • 一時的な大量アクセスがある場合 → イベント前にプロビジョンドの容量増強

6. 便利な追加機能

6.1 TTL(Time To Live)- データの賞味期限設定

  • データに有効期限をつけて自動削除
  • セッション情報や一時データに最適
  • バックアップコストの削減にも効果的
// TTLの例:24時間後に削除される一時トークン
{
  "token_id": "abc123",
  "token_value": "xyz789",
  "user_id": "123",
  "expiration": 1711392000  // Unix時間
}

6.2 DAX(DynamoDB Accelerator)

  • インメモリキャッシュでめっちゃ高速化(マイクロ秒単位)
  • 読み取りの多いワークロードに有効
  • 実装は簡単(SDK差し替えるだけ)

6.3 トランザクション

  • 複数の操作を一括で実行(全部成功か全部失敗か)
  • 例:在庫減らす+注文確定など
  • RDBMSのACID特性に近い安全性

6.4 ストリーム

  • テーブルの変更をリアルタイムで捕捉
  • Lambda関数との連携で様々な自動処理が可能
  • 例:注文テーブル更新→自動メール送信

6.5 グローバルテーブル

  • 世界中の複数リージョンにデータを自動複製
  • 低レイテンシーのグローバルアプリに最適
  • マルチリージョン障害対策にも

7. DynamoDBの設計アプローチ(超重要)

7.1 RDBとDynamoDBの設計思想の違い

  • RDB→「データをどう保存するか」から考える
  • DynamoDB→「データをどう取り出すか」から考える

7.2 設計ステップ

  1. アクセスパターンを洗い出す(どんなクエリが必要か全部リストアップ)
  2. キー構造を設計(パーティションキー、ソートキー)
  3. 必要なインデックスを計画(GSI、LSI)
  4. 非正規化を検討(JOIN操作がないので、データを複製することも)

7.3 よくある設計パターン

シングルテーブルデザイン

  • 複数のエンティティを1つのテーブルに格納
  • PKとSKの組み合わせでエンティティを区別
  • 一見複雑だが、パフォーマンスが高く、JOINが不要に
// ユーザーアイテム
{
  "PK": "USER#123",
  "SK": "PROFILE",
  "name": "田中太郎",
  "email": "tanaka@example.com"
}

// 同じユーザーの注文アイテム
{
  "PK": "USER#123",
  "SK": "ORDER#456",
  "orderDate": "2025-03-24",
  "amount": 5000
}

GSIオーバーロード

  • 1つのGSIを複数の用途で利用
  • データ属性を使い分けることで様々なクエリを実現
// GSI1-PK と GSI1-SK を複数の用途で使い回す
// 例: 商品検索用
{
  "PK": "PRODUCT#789",
  "SK": "METADATA",
  "name": "ゲーミングPC",
  "GSI1-PK": "CATEGORY#electronics",
  "GSI1-SK": "PRODUCT#ゲーミングPC"
}

// 例: メール検索用
{
  "PK": "USER#123",
  "SK": "PROFILE",
  "name": "田中太郎",
  "email": "tanaka@example.com",
  "GSI1-PK": "EMAIL#tanaka@example.com",
  "GSI1-SK": "USER#123"
}

8. DynamoDB vs RDS 選択ガイド

8.1 DynamoDBが最適なケース

  • 高いスケーラビリティが必要
  • シンプルなキーバリュー検索が中心
  • 低レイテンシーが絶対条件
  • サーバーレスアーキテクチャとの連携

8.2 RDSが最適なケース

  • 複雑なJOINクエリが必要
  • トランザクション整合性が重要
  • 複雑な分析や集計が必要
  • SQLの専門知識を持つチームがある

8.3 実ユースケース例

DynamoDBが向いている例

  • セッション管理(高速アクセス)
  • ユーザープロファイル
  • ゲームのスコアボード
  • IoTデータの収集
  • リアルタイムダッシュボード

RDSが向いている例

  • 財務システム
  • 複雑な検索が必要なECサイト
  • BI/分析システム
  • 複雑なレポート生成システム

9. コスト最適化のコツ

9.1 DynamoDBのコスト要素

  • 読み取り/書き込みキャパシティ
  • ストレージ量
  • データ転送量
  • バックアップと追加機能

9.2 コスト削減テクニック

  • 適切なキャパシティモードの選択
  • 自動スケーリングの設定(プロビジョンドの場合)
  • 不要なGSIの削除(GSIもコストがかかる)
  • TTLで不要データを自動削除
  • 大量読み取りにはDAXの検討

10. よくあるトラブルと対策

10.1 ホットパーティション問題

  • 特定のパーティションに負荷が集中する問題
  • 対策:パーティションキーの再設計、ハッシュ接尾辞の追加など
// 不均一なアクセスを分散させる例
// user_id + 乱数 でパーティションキーを作成
const randomSuffix = Math.floor(Math.random() * 10);
const partitionKey = `${userId}_${randomSuffix}`;

10.2 スロットリング(リクエスト制限)

  • キャパシティを超過するとリクエストが拒否される
  • 対策:指数バックオフ&リトライ、キャパシティ増強

10.3 スキャン操作の非効率性

  • 全テーブルスキャンは避ける
  • 対策:パラレルスキャン、ページネーション、GSIの活用

11. DynamoDBを使うときの実践的なTips

11.1 開発環境のセットアップ

  • ローカル開発には「DynamoDB Local」を使える
  • AWS CLIとDynamoDB管理ツール(NoSQL Workbench)の活用

11.2 データのバックアップ

  • オンデマンドバックアップの活用
  • Point-in-Time Recovery(PITR)の設定

11.3 コーディングのベストプラクティス

  • バッチ操作の活用(BatchGetItem, BatchWriteItem)
  • 条件付き書き込みで楽観的ロックを実装
  • SDKのページネーション機能を活用

12. まとめ:これだけ覚えておけばOK

  1. DynamoDBはスケーラビリティとパフォーマンスに優れたNoSQLデータベース
  2. 「どうデータを取り出すか」から設計を始める
  3. パーティションキーの選定がパフォーマンスの鍵
  4. JOINがないので、アクセスパターンに合わせた非正規化が必要
  5. GSIを効果的に活用して複数の検索パターンを実現
  6. コスト管理は、使用パターンに合わせたモード選択と定期的な見直しが重要

これでDynamoDBの基本から実践的な活用方法まで、コンパクトに理解できるようになりました。どんどん実際に使ってみて、経験を積んでいくことをオススメします!

1

Discussion