コンテンツにスキップ

SQLユーザーがDynamoDBを使うと嵌まるポイント

概要

SQL に慣れたエンジニアが DynamoDB を使い始めると「SQL と同じ感覚で使うとハマる」ポイントが多い。DevelopersIO の記事を紹介する形で話題になった投稿。

詳細

SQL との根本的な違い

SQL(RDB):
  → スキーマを先に決める
  → JOIN で柔軟にデータを取得
  → WHERE 句で任意のカラムを条件にできる
  → テーブルスキャンは遅いがとりあえず動く

DynamoDB(NoSQL):
  → スキーマレス(カラムは後から追加可能)
  → JOIN は存在しない(アプリケーション側で結合)
  → PK/SK 以外の条件でのクエリは GSI が必要
  → フルスキャンは超高額 + 遅い(使ってはいけない)

嵌まりやすいポイント

1. PK 設計を後から変えられない

テーブル作成後に PK(Partition Key)を変えることはできない。
アクセスパターンを先にリストアップして PK を設計することが必須。

悪い設計: users テーブルに pk = user_id だけ
→ 「会社に所属するユーザー一覧を取得」しようとすると全スキャン

良い設計: GSI を追加して company_id でも検索できるようにする

2. フルスキャンは避ける

// 絶対にやってはいけない(小規模なら動くが本番ではコスト爆発)
result, err := client.Scan(ctx, &dynamodb.ScanInput{
    TableName: aws.String("users"),
})

// 正しい: Query で PK または GSI を使う
result, err := client.Query(ctx, &dynamodb.QueryInput{
    TableName: aws.String("users"),
    KeyConditionExpression: aws.String("pk = :pk"),
    ExpressionAttributeValues: map[string]types.AttributeValue{
        ":pk": &types.AttributeValueMemberS{Value: "USER#" + userID},
    },
})

3. 1リクエスト 1MB 制限

Query/Scan の1回のレスポンスは最大 1MB。
1MB を超えると LastEvaluatedKey が返る → ページネーションが必要。

for {
    result, _ := client.Query(ctx, input)
    // 処理...
    if result.LastEvaluatedKey == nil { break }
    input.ExclusiveStartKey = result.LastEvaluatedKey
}

4. トランザクションは2テーブル・25アイテムまで

DynamoDB トランザクションは TransactWriteItems で最大 25 アイテムまで。複雑なトランザクションは RDB の方が向いている。

DynamoDB が向いているケース

✓ キー・バリューの単純な読み書き(超高スループット)
✓ セッション管理(TTL で自動削除)
✓ リアルタイムゲームのスコアボード
✓ IoT デバイスのセンサーデータ

✗ 複雑な JOIN が必要なデータ
✗ アクセスパターンが不明確
✗ 集計クエリが多い

なぜ重要か / いつ使うか

  • AWS で NoSQL を選定するとき
  • 既存 RDB を DynamoDB に移行することを検討するとき
  • DynamoDB の設計レビューで「スキャンしていないか」を確認するとき