Tasuke Hubのロゴ

ITを中心に困っている人を助けるメディア

分かりやすく解決策を提供することで、あなたの困ったをサポート。 全ての人々がスムーズに生活できる世界を目指します。

DynamoDBを初めて触る方必見!基本から実践までわかりやすく解説

記事のサムネイル

DynamoDBとは?AWSが提供するNoSQLデータベースの特徴

DynamoDBとは、Amazonが提供するフルマネージド型のNoSQLデータベースサービスです。従来のリレーショナルデータベース(RDB)とは異なり、スケーラビリティが高く、高速なパフォーマンスが特徴です。

DynamoDBの主な特徴は以下の通りです:

  • 高いスケーラビリティ: 数TBから数PBまで、データ量に応じて自動的にスケールします
  • 高速なパフォーマンス: 1桁ミリ秒の応答時間を維持し、高速なデータアクセスを実現
  • フルマネージド: サーバーのプロビジョニングや管理が不要で、運用コストを削減
  • 高い信頼性: 複数のアベイラビリティゾーンにデータを自動的にレプリケーションし、高い可用性を実現
  • 柔軟なデータモデル: 厳格なスキーマを必要とせず、アプリケーションの進化に合わせて柔軟に変更可能

NoSQLデータベースとしてのDynamoDBは、キーバリュー形式やドキュメント形式でデータを格納します。これにより、RDBのような複雑な結合操作は苦手ですが、単純なキー検索や範囲検索においては非常に高速なパフォーマンスを発揮します。

// DynamoDBのデータ例(JSON形式)
{
  "UserId": "user123",
  "UserName": "山田太郎",
  "Email": "[email protected]",
  "Age": 30,
  "Interests": ["プログラミング", "読書", "旅行"],
  "LastLogin": "2025-05-10T14:30:00Z"
}

DynamoDBは特に以下のようなユースケースに適しています:

  • 大規模Webアプリケーション: ユーザープロファイル、セッション管理など
  • モバイルアプリのバックエンド: ユーザーデータの保存と高速アクセス
  • ゲームアプリケーション: プレイヤーデータやゲーム状態の管理
  • IoTデータ処理: センサーデータの収集と分析
  • マイクロサービスアーキテクチャ: サービス間のデータ共有

RDBとの大きな違いは、DynamoDBではテーブル間の関係(リレーション)を直接表現するのではなく、データの非正規化やGSI(Global Secondary Index)などの機能を使ってデータアクセスパターンに最適化した設計を行う点です。

テーブル設計の基本とプライマリキーの選び方

DynamoDBでのテーブル設計は、従来のRDBとは大きく異なります。DynamoDBでは「アクセスパターン」を最重視した設計が必要です。つまり、「どのようにデータを検索するか」を先に考えてから、テーブル構造を決めていきます。

プライマリキーの重要性

DynamoDBにおけるプライマリキーは、データへのアクセス方法を決定する最も重要な要素です。プライマリキーには以下の2種類があります:

  1. 単一のパーティションキー(Simple Primary Key):

    • 一意な値を持つ単一の属性
    • 例:ユーザーIDやプロダクトID
  2. パーティションキーとソートキーの複合キー(Composite Primary Key):

    • パーティションキー + ソートキー
    • 同じパーティションキーを持つ複数のアイテムをソートキーで並べ替え可能
// 単一のパーティションキーの例
{
  "ProductId": "p123",  // パーティションキー
  "Name": "スマートフォン",
  "Price": 50000,
  "Category": "電子機器"
}

// 複合キーの例
{
  "UserId": "u456",     // パーティションキー
  "OrderDate": "2025-01-15",  // ソートキー
  "OrderId": "o789",
  "Amount": 25000,
  "Items": ["スマートフォン", "ケース"]
}

テーブル設計の基本原則

  1. 一つのテーブルに複数のエンティティを格納する

    • RDBのような「一つのエンティティ = 一つのテーブル」という考え方を捨てる
    • 関連するデータを一つのテーブルにまとめることで、結合操作を減らす
  2. アクセスパターンを最優先に考える

    • 「どのようにデータを取得するか」をまず考える
    • よく使うクエリを高速に実行できるようにキーを設計する
  3. データを非正規化する

    • データの重複を許容し、クエリを単純化する
    • 例:ユーザーの住所情報を注文情報にも保存する
  4. GSI(Global Secondary Index)を活用する

    • 複数の視点でデータにアクセスするために2次インデックスを作成
    • 例:ユーザーIDでの検索だけでなく、メールアドレスでも検索できるようにする

良いプライマリキーの選び方

  1. 高いカーディナリティ(一意性)を持つ属性を選ぶ

    • 値が広く分散している属性が理想的
    • 例:UUID、タイムスタンプ+ランダム文字列など
  2. ホットパーティションを避ける

    • 特定のパーティションに負荷が集中しないよう設計
    • 例:日付を単独のパーティションキーにすると、最新の日付に負荷が集中
  3. アプリケーションの主要なアクセスパターンをサポートする

    • 最も頻繁に行うクエリが効率的になるようにキーを選択

DynamoDBの初心者がよく陥る罠は、RDBと同じ考え方でテーブル設計を行うことです。DynamoDBでは「データ構造よりもアクセスパターン」を優先して設計することが成功の鍵です。

DynamoDBを始める第一歩:テーブル作成と基本操作

DynamoDBを実際に使ってみましょう。最初に必要なのはAWSアカウント(無料利用枠で十分です)です。AWSアカウントを取得したら、DynamoDBの基本操作を見ていきましょう。

テーブルの作成方法

DynamoDBのテーブルはAWSコンソール、AWS CLI、SDKのいずれかを使って作成できます。ここではAWSコンソールでの作成方法を説明します。

  1. AWSコンソールにサインインし、「DynamoDB」サービスを選択
  2. 「テーブルの作成」をクリック
  3. 以下の情報を入力:
    • テーブル名:例えば「Users」
    • パーティションキー:例えば「UserId」(文字列)
    • (オプション)ソートキー:例えば「CreatedAt」(文字列)
  4. デフォルト設定のままで「テーブルの作成」をクリック

SDKでテーブルを作成する場合は、次のようなコードを使用します:

// Node.jsでのテーブル作成例
const AWS = require('aws-sdk');
AWS.config.update({ region: 'ap-northeast-1' });
const dynamodb = new AWS.DynamoDB();

const params = {
  TableName: 'Users',
  KeySchema: [
    { AttributeName: 'UserId', KeyType: 'HASH' },  // パーティションキー
    { AttributeName: 'CreatedAt', KeyType: 'RANGE' }  // ソートキー
  ],
  AttributeDefinitions: [
    { AttributeName: 'UserId', AttributeType: 'S' },
    { AttributeName: 'CreatedAt', AttributeType: 'S' }
  ],
  ProvisionedThroughput: {
    ReadCapacityUnits: 5,
    WriteCapacityUnits: 5
  }
};

dynamodb.createTable(params, (err, data) => {
  if (err) console.error('テーブル作成エラー:', JSON.stringify(err));
  else console.log('テーブル作成成功:', JSON.stringify(data));
});

データの追加・更新方法

テーブルを作成したら、データを追加してみましょう。DynamoDBには次のような基本操作があります:

  1. PutItem: アイテムを新規追加または上書き
  2. UpdateItem: 既存アイテムの特定の属性を更新
  3. DeleteItem: アイテムを削除
  4. GetItem: キーでアイテムを取得
  5. Query: パーティションキーで検索(ソートキーで範囲も指定可能)
  6. Scan: テーブル全体をスキャン

以下はNode.jsのDynamoDB Document Clientを使ったデータ操作の例です:

// DynamoDB Document Clientの利用例
const AWS = require('aws-sdk');
AWS.config.update({ region: 'ap-northeast-1' });
const docClient = new AWS.DynamoDB.DocumentClient();

// 1. データの追加(PutItem)
const addUser = async () => {
  const params = {
    TableName: 'Users',
    Item: {
      UserId: 'user123',
      CreatedAt: '2025-05-14T10:00:00Z',
      Name: '鈴木一郎',
      Age: 28,
      Email: '[email protected]',
      Interests: ['スポーツ', '旅行']
    }
  };
  
  try {
    await docClient.put(params).promise();
    console.log('ユーザーを追加しました');
  } catch (err) {
    console.error('エラー:', err);
  }
};

// 2. データの取得(GetItem)
const getUser = async (userId, createdAt) => {
  const params = {
    TableName: 'Users',
    Key: {
      UserId: userId,
      CreatedAt: createdAt
    }
  };
  
  try {
    const data = await docClient.get(params).promise();
    console.log('ユーザーデータ:', data.Item);
    return data.Item;
  } catch (err) {
    console.error('エラー:', err);
  }
};

// 3. データの更新(UpdateItem)
const updateUser = async (userId, createdAt, newAge) => {
  const params = {
    TableName: 'Users',
    Key: {
      UserId: userId,
      CreatedAt: createdAt
    },
    UpdateExpression: 'set Age = :age',
    ExpressionAttributeValues: {
      ':age': newAge
    },
    ReturnValues: 'UPDATED_NEW'
  };
  
  try {
    const data = await docClient.update(params).promise();
    console.log('更新された属性:', data.Attributes);
  } catch (err) {
    console.error('エラー:', err);
  }
};

DynamoDBローカル環境でのテスト

開発中は、AWS上の実際のDynamoDBを使う前にローカル環境でテストするのが便利です。DynamoDB Localを使えば、料金を気にせず開発できます。

  1. Docker を使ったDynamoDB Localの起動:
docker run -p 8000:8000 amazon/dynamodb-local
  1. ローカルDynamoDBへの接続設定:
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({
  region: 'localhost',
  endpoint: 'http://localhost:8000',
  accessKeyId: 'dummy',  // ローカルなのでダミー値でOK
  secretAccessKey: 'dummy'
});

初めてDynamoDBを触る際は、まずはローカル環境で様々な操作を試してみることをおすすめします。操作に慣れてから実際のAWS環境に移行すると、余計なコストを避けられます。

クエリとスキャン:効率的なデータ取得方法を理解する

DynamoDBからデータを取得する方法は主に二つあります:「クエリ(Query)」と「スキャン(Scan)」です。これらの違いとベストプラクティスを理解することで、効率的なデータ取得が可能になります。

クエリ操作

クエリはパーティションキーを指定して、そのパーティション内のデータを検索します。ソートキーを使用すれば範囲検索も可能です。

クエリ操作の特徴:

  • パーティションキーの指定が必須
  • ソートキーを使って範囲検索や並べ替えが可能
  • インデックスが効くため非常に高速
  • 必要なデータだけを読み取るため、コスト効率が良い
// Query操作の例(特定ユーザーの注文履歴を取得)
const getUserOrders = async (userId) => {
  const params = {
    TableName: 'Orders',
    KeyConditionExpression: 'UserId = :uid',
    ExpressionAttributeValues: {
      ':uid': userId
    }
  };
  
  try {
    const data = await docClient.query(params).promise();
    console.log(`${data.Items.length}件の注文が見つかりました`);
    return data.Items;
  } catch (err) {
    console.error('クエリエラー:', err);
  }
};

// ソートキーで範囲検索(日付範囲を指定)
const getOrdersByDateRange = async (userId, startDate, endDate) => {
  const params = {
    TableName: 'Orders',
    KeyConditionExpression: 'UserId = :uid AND OrderDate BETWEEN :start AND :end',
    ExpressionAttributeValues: {
      ':uid': userId,
      ':start': startDate,
      ':end': endDate
    }
  };
  
  try {
    const data = await docClient.query(params).promise();
    console.log(`${data.Items.length}件の注文が見つかりました`);
    return data.Items;
  } catch (err) {
    console.error('クエリエラー:', err);
  }
};

スキャン操作

スキャンはテーブル全体を探索し、指定した条件に一致するアイテムを見つけます。

スキャン操作の特徴:

  • テーブル内のすべてのアイテムを検査する
  • パーティションキーやソートキーに関係なく検索できる
  • インデックスを使用しないため、大きなテーブルでは非常に遅く、コスト高
  • データ量の少ないテーブルか、バッチ処理向き
// Scan操作の例(特定の価格以上の商品を取得)
const getExpensiveProducts = async (minPrice) => {
  const params = {
    TableName: 'Products',
    FilterExpression: 'Price >= :price',
    ExpressionAttributeValues: {
      ':price': minPrice
    }
  };
  
  try {
    const data = await docClient.scan(params).promise();
    console.log(`${data.Items.length}件の商品が見つかりました`);
    return data.Items;
  } catch (err) {
    console.error('スキャンエラー:', err);
  }
};

クエリとスキャンのベストプラクティス

  1. 可能な限りクエリを使用する

    • スキャンよりもクエリの方が常に効率的
    • テーブル設計時にアクセスパターンを考慮し、クエリが使えるように設計する
  2. 必要な属性だけを取得する

    • ProjectionExpressionを使って必要な属性だけを指定する
    ProjectionExpression: 'UserId, OrderDate, Amount'
  3. ページネーションを活用する

    • 大量のデータを一度に取得しない
    • LastEvaluatedKeyとLimitパラメータでページングを実装
    // ページネーションの例
    const scanWithPagination = async (limit = 20) => {
      let lastKey = null;
      let allItems = [];
      
      do {
        const params = {
          TableName: 'Products',
          Limit: limit
        };
        
        // 前回のLastEvaluatedKeyがあれば追加
        if (lastKey) params.ExclusiveStartKey = lastKey;
        
        const data = await docClient.scan(params).promise();
        allItems = [...allItems, ...data.Items];
        lastKey = data.LastEvaluatedKey;
        
        console.log(`${data.Items.length}件取得しました`);
      } while (lastKey); // LastEvaluatedKeyがnullになるまで繰り返す
      
      return allItems;
    };
  4. パラレルスキャンを利用する

    • 大きなテーブルをスキャンする場合は、複数のワーカーで並列処理
    • Segment(分割数)とTotalSegment(総分割数)を指定
  5. GSI(Global Secondary Index)を活用する

    • パーティションキー以外の属性での検索が必要な場合はGSIを作成
    • GSIがあれば、スキャンの代わりにクエリを使用可能に

DynamoDBを効率的に使いこなすには、このクエリとスキャンの違いを理解し、適切に使い分けることが重要です。基本的には可能な限りスキャンを避け、クエリを使うようにテーブル設計することを心がけましょう。

DynamoDBのスケーリングと料金体系の把握

DynamoDBの大きな特徴の一つは、トラフィックに応じて自動的にスケールする能力です。しかし、効率的に使うためには、スケーリングの仕組みと料金体系を理解することが重要です。

キャパシティモード

DynamoDBには2つのキャパシティモードがあります:

  1. プロビジョンドキャパシティモード

    • 事前に読み込み/書き込み容量ユニットを設定
    • 予測可能なトラフィックパターンに適している
    • 自動スケーリングと組み合わせることも可能
  2. オンデマンドキャパシティモード

    • 使った分だけ支払う従量課金制
    • トラフィックの予測が難しい場合に適している
    • セットアップが簡単だが、単価は高め

キャパシティユニットとは

DynamoDBの処理能力は以下の単位で測定されます:

  • 読み込みキャパシティユニット(RCU): 1秒あたり最大4KBのアイテムを1回の強い整合性のある読み込み、または2回の結果整合性のある読み込み
  • 書き込みキャパシティユニット(WCU): 1秒あたり最大1KBのアイテムを1回書き込み
// 例:5KB のアイテムを書き込むには?
const itemSizeKB = 5;
const requiredWCUs = Math.ceil(itemSizeKB / 1); // 5 WCU 必要

// 例:8KB のアイテムを強い整合性で読み込むには?
const itemSizeKB = 8;
const requiredRCUs = Math.ceil(itemSizeKB / 4); // 2 RCU 必要

自動スケーリングの設定

プロビジョンドキャパシティモードでは、自動スケーリングを設定することで、トラフィックに応じてキャパシティを動的に調整できます:

  1. AWSコンソールでの設定方法

    • テーブルの「キャパシティ」タブ
    • 「自動スケーリングを管理」をクリック
    • 最小/最大キャパシティと目標使用率を設定(通常70%程度)
  2. AWS CLIでの設定例

# Application Auto Scalingのターゲットリソースを登録
aws application-autoscaling register-scalable-target \
  --service-namespace dynamodb \
  --resource-id "table/Users" \
  --scalable-dimension "dynamodb:table:ReadCapacityUnits" \
  --min-capacity 5 \
  --max-capacity 100

# スケーリングポリシーを設定
aws application-autoscaling put-scaling-policy \
  --service-namespace dynamodb \
  --resource-id "table/Users" \
  --scalable-dimension "dynamodb:table:ReadCapacityUnits" \
  --policy-name "UsersReadCapacityScaling" \
  --policy-type "TargetTrackingScaling" \
  --target-tracking-scaling-policy-configuration '{
    "TargetValue": 70.0,
    "PredefinedMetricSpecification": {
      "PredefinedMetricType": "DynamoDBReadCapacityUtilization"
    }
  }'

DynamoDBの料金体系

DynamoDBの料金は主に以下の要素で構成されます:

  1. データストレージ料金

    • 保存されるデータのGBあたりの月額料金
    • 2025年5月時点で約$0.25/GB/月(リージョンにより異なる)
  2. 読み取り/書き込み容量料金

    • プロビジョンドモード:予約したRCU/WCUに対して課金
    • オンデマンドモード:実際に使用したリクエスト数に対して課金
  3. データ転送料金

    • リージョン間のレプリケーション
    • インターネットへのデータ転送(同一リージョン内は無料)
  4. バックアップと復元の料金

    • オンデマンドバックアップのストレージ料金
    • 連続バックアップ(PITR)の追加料金
  5. グローバルテーブルの料金

    • レプリケートされたデータの書き込み容量単位
    • レプリケーションデータ転送料金

コスト最適化のポイント

  1. 適切なキャパシティモードの選択

    • 安定したトラフィック → プロビジョンドモード
    • 予測困難なトラフィック → オンデマンドモード
  2. TTL(有効期限)の活用

    • 不要になったデータを自動的に削除
    • 古いデータを保持するためのストレージコストを削減
// TTLの設定例
const params = {
  TableName: 'SessionData',
  TimeToLiveSpecification: {
    Enabled: true,
    AttributeName: 'ExpirationTime' // UNIX時間で設定された属性
  }
};

dynamodb.updateTimeToLive(params, (err, data) => {
  if (err) console.error('TTL設定エラー:', err);
  else console.log('TTL設定成功:', data);
});
  1. DAX(DynamoDB Accelerator)の検討

    • 読み取りが多いワークロードにキャッシュレイヤーを提供
    • 読み取りコストを大幅に削減可能
  2. リザーブドキャパシティの検討

    • 長期的に安定した使用量がある場合
    • 1年または3年の契約で大幅な割引が可能
  3. GSIの最適化

    • 必要最小限のGSIのみ作成
    • プロジェクション属性を必要なものだけに制限

DynamoDBのコストを効率的に管理するには、AWS Cost Explorerを定期的に確認し、リソースの使用状況を監視することをおすすめします。また、AWS Budgetsを設定して、予期せぬコスト増加を検知することも重要です。

サンプルプロジェクトで学ぶDynamoDBの実践的な使い方

ここまでDynamoDBの基本概念を学んできましたが、実際のプロジェクトでどのように使うのか見ていきましょう。シンプルなTodoアプリケーションを例に、DynamoDBの実践的な使い方を解説します。

プロジェクト概要:Todoアプリケーション

このTodoアプリでは以下の機能を実装します:

  • ユーザー登録とログイン
  • Todoアイテムの作成、表示、更新、削除
  • カテゴリーでのTodoアイテムのフィルタリング
  • 期限が近いTodoアイテムの通知

テーブル設計

アクセスパターンを考慮して、シングルテーブル設計を採用します:

// TodoTable設計
{
  PK: String,       // パーティションキー:エンティティタイプ+ID (例: "USER#123", "TODO#456")
  SK: String,       // ソートキー:エンティティタイプ+メタデータ (例: "PROFILE", "TODO#2025-05-20")
  Type: String,     // エンティティタイプ ("USER", "TODO")
  Data: Map,        // エンティティのデータ
  GSI1PK: String,   // GSI1のパーティションキー
  GSI1SK: String    // GSI1のソートキー
}

データモデルの例

  1. ユーザープロファイル:
{
  PK: "USER#u123",            // ユーザーID
  SK: "PROFILE",              // 固定値
  Type: "USER",
  Data: {
    Email: "[email protected]",
    Name: "テストユーザー",
    CreatedAt: "2025-05-14T12:00:00Z"
  }
}
  1. Todoアイテム:
{
  PK: "USER#u123",            // 所有者のユーザーID
  SK: "TODO#t456",            // TodoのID
  Type: "TODO",
  Data: {
    Title: "牛乳を買う",
    Description: "スーパーで低脂肪牛乳を1L",
    Category: "買い物",
    DueDate: "2025-05-16T18:00:00Z",
    Status: "PENDING",
    CreatedAt: "2025-05-14T12:30:00Z"
  },
  GSI1PK: "TODO#CATEGORY#買い物",  // カテゴリーでの検索用
  GSI1SK: "2025-05-16T18:00:00Z"   // 期限でのソート用
}

APIの実装例

Node.jsとExpressで簡単なAPIを実装してみましょう:

// app.js
const express = require('express');
const AWS = require('aws-sdk');
const { v4: uuidv4 } = require('uuid');
const app = express();

// DynamoDB設定
AWS.config.update({ region: 'ap-northeast-1' });
const docClient = new AWS.DynamoDB.DocumentClient();
const TABLE_NAME = 'TodoTable';

app.use(express.json());

// ユーザー登録API
app.post('/users', async (req, res) => {
  const { email, name } = req.body;
  const userId = `u${uuidv4().substring(0, 8)}`;
  
  const params = {
    TableName: TABLE_NAME,
    Item: {
      PK: `USER#${userId}`,
      SK: 'PROFILE',
      Type: 'USER',
      Data: {
        Email: email,
        Name: name,
        CreatedAt: new Date().toISOString()
      }
    }
  };
  
  try {
    await docClient.put(params).promise();
    res.status(201).json({ userId, message: 'ユーザーが作成されました' });
  } catch (err) {
    console.error('エラー:', err);
    res.status(500).json({ error: 'ユーザー作成に失敗しました' });
  }
});

// Todo作成API
app.post('/users/:userId/todos', async (req, res) => {
  const { userId } = req.params;
  const { title, description, category, dueDate } = req.body;
  const todoId = `t${uuidv4().substring(0, 8)}`;
  
  const params = {
    TableName: TABLE_NAME,
    Item: {
      PK: `USER#${userId}`,
      SK: `TODO#${todoId}`,
      Type: 'TODO',
      Data: {
        Title: title,
        Description: description,
        Category: category,
        DueDate: dueDate,
        Status: 'PENDING',
        CreatedAt: new Date().toISOString()
      },
      GSI1PK: `TODO#CATEGORY#${category}`,
      GSI1SK: dueDate
    }
  };
  
  try {
    await docClient.put(params).promise();
    res.status(201).json({ todoId, message: 'Todoが作成されました' });
  } catch (err) {
    console.error('エラー:', err);
    res.status(500).json({ error: 'Todo作成に失敗しました' });
  }
});

// ユーザーのTodo一覧取得API
app.get('/users/:userId/todos', async (req, res) => {
  const { userId } = req.params;
  
  const params = {
    TableName: TABLE_NAME,
    KeyConditionExpression: 'PK = :pk AND begins_with(SK, :sk)',
    ExpressionAttributeValues: {
      ':pk': `USER#${userId}`,
      ':sk': 'TODO#'
    }
  };
  
  try {
    const data = await docClient.query(params).promise();
    const todos = data.Items.map(item => ({
      todoId: item.SK.split('#')[1],
      ...item.Data
    }));
    
    res.json(todos);
  } catch (err) {
    console.error('エラー:', err);
    res.status(500).json({ error: 'Todo一覧の取得に失敗しました' });
  }
});

// カテゴリーでTodoをフィルターするAPI(GSI1を使用)
app.get('/todos/by-category/:category', async (req, res) => {
  const { category } = req.params;
  
  const params = {
    TableName: TABLE_NAME,
    IndexName: 'GSI1',
    KeyConditionExpression: 'GSI1PK = :pk',
    ExpressionAttributeValues: {
      ':pk': `TODO#CATEGORY#${category}`
    }
  };
  
  try {
    const data = await docClient.query(params).promise();
    const todos = data.Items.map(item => ({
      todoId: item.SK.split('#')[1],
      userId: item.PK.split('#')[1],
      ...item.Data
    }));
    
    res.json(todos);
  } catch (err) {
    console.error('エラー:', err);
    res.status(500).json({ error: 'Todo一覧の取得に失敗しました' });
  }
});

// サーバー起動
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`サーバーが起動しました: http://localhost:${PORT}`);
});

デプロイと運用のヒント

  1. IAMロールとポリシー

    • DynamoDBアクセス用の適切なIAMポリシーを作成する
    • 最小特権の原則に従い、必要な操作のみ許可する
  2. CloudFormationによるインフラのコード化

    • テーブル設定をCloudFormationで管理すると再現性が高まる
    Resources:
      TodoTable:
        Type: AWS::DynamoDB::Table
        Properties:
          TableName: TodoTable
          BillingMode: PAY_PER_REQUEST
          AttributeDefinitions:
            - AttributeName: PK
              AttributeType: S
            - AttributeName: SK
              AttributeType: S
            - AttributeName: GSI1PK
              AttributeType: S
            - AttributeName: GSI1SK
              AttributeType: S
          KeySchema:
            - AttributeName: PK
              KeyType: HASH
            - AttributeName: SK
              KeyType: RANGE
          GlobalSecondaryIndexes:
            - IndexName: GSI1
              KeySchema:
                - AttributeName: GSI1PK
                  KeyType: HASH
                - AttributeName: GSI1SK
                  KeyType: RANGE
              Projection:
                ProjectionType: ALL
  3. DynamoDB Streamsの活用

    • 重要なデータ変更をキャプチャし、Lambdaで処理する
    • 例:期限間近のTodoを検出し、SNSで通知を送信
  4. パフォーマンスモニタリング

    • CloudWatchメトリクスでスロットリングを監視
    • X-Ray統合でボトルネックを検出

ベストプラクティス

  1. バッチ処理の活用

    • BatchGetItem, BatchWriteItemで複数操作をまとめる
    • TransactWriteItemsで複数テーブルにまたがるトランザクションを実現
  2. エラー処理とリトライロジック

    • 再試行可能なエラー(ProvisionedThroughputExceededException など)に対してエクスポネンシャルバックオフを実装
  3. キャッシュレイヤーの導入

    • 頻繁に読み取られるデータには、DAXやRedisなどのキャッシュを検討
  4. コスト管理

    • AWS Cost Explorerで定期的にコストを確認
    • 使用率の低いGSIは削除または最適化

このサンプルプロジェクトを通じて、DynamoDBの実践的な使い方を学べました。実際のアプリケーション開発では、こうした基本パターンを応用しながら、より複雑なデータモデルや高度な機能を実装していくことになります。

DynamoDBはシンプルな操作から始めて徐々に機能を拡張していけるため、小規模なプロジェクトから始めて、経験を積みながらスキルを向上させていくことをおすすめします。

TH

Tasuke Hub管理人

東証プライム市場上場企業エンジニア

情報系修士卒業後、大手IT企業にてフルスタックエンジニアとして活躍。 Webアプリケーション開発からクラウドインフラ構築まで幅広い技術に精通し、 複数のプロジェクトでリードエンジニアを担当。 技術ブログやオープンソースへの貢献を通じて、日本のIT技術コミュニティに積極的に関わっている。

🎓情報系修士🏢東証プライム上場企業💻フルスタックエンジニア📝技術ブログ執筆者

おすすめの書籍

おすすめコンテンツ