DynamoDBをSQLと同じ感覚で使うとハマるポイント
概要¶
SQLに慣れたエンジニアがDynamoDBを初めて使うと、RDB的な思考パターンのままでハマりがち。DynamoDBはNoSQLであり設計思想がRDBと根本的に異なる。
SQLとDynamoDBの主な違い¶
| 観点 | SQL (RDB) | DynamoDB |
|---|---|---|
| スキーマ | 固定スキーマ | スキーマレス(アイテムごとに異なる属性可) |
| クエリの柔軟性 | JOINや任意条件で検索可 | Primary Key / GSI での検索のみ |
| トランザクション | 複数テーブルをまたぐACID | 同一テーブル内の限定的トランザクション |
| スケーリング | 垂直スケーリングが基本 | 水平スケーリング(無制限) |
| コスト構造 | インスタンスコスト | 読み書きユニット数課金 |
| JOIN | 自由にできる | できない(アプリ側で処理) |
| インデックス | 任意カラムに追加可 | GSI(グローバルセカンダリインデックス)で代替 |
よくハマるポイントと対処法¶
1. 「とりあえず検索」ができない¶
# SQLなら:
SELECT * FROM users WHERE age > 30 AND city = 'Tokyo'
# DynamoDBではこれは Scan(全件取得後フィルタ)になる
# → テーブルが大きいと爆遅・爆課金
response = table.scan(
FilterExpression=Attr('age').gt(30) & Attr('city').eq('Tokyo')
)
# NG: データ量に比例してコストが上がる
対処法: アクセスパターンを先に決めてから設計する。ageやcityで頻繁に検索するなら GSI を作る。
# GSI を使ったクエリ(効率的)
response = table.query(
IndexName='city-age-index',
KeyConditionExpression=Key('city').eq('Tokyo') & Key('age').gt(30)
)
2. Primary Key の設計が全て¶
DynamoDBはPrimary Key(Partition Key + Sort Key)の設計で性能が決まる。
悪い設計:
PK: user_id (単調増加するID)
→ 特定のパーティションに書き込みが集中(ホットパーティション)
良い設計:
PK: user_id#timestamp のようにハッシュを分散させる
SK: 日時や種別でソートできる値
3. JOINはできない¶
-- SQLなら1クエリで済む
SELECT u.name, o.amount
FROM users u JOIN orders o ON u.id = o.user_id
WHERE u.id = 123
対処法: 1テーブル設計(Single Table Design)でエンティティを同一テーブルに格納するか、非正規化して冗長に持つ。
4. 読み書きキャパシティの設計¶
RCU(Read Capacity Unit): 1RCU = 最大4KBの強い整合性読み取り1回
WCU(Write Capacity Unit): 1WCU = 最大1KBの書き込み1回
→ アイテムサイズが大きいと消費ユニットが増える
→ バースト時のキャパシティ計算が必要
5. トランザクションの制約¶
# DynamoDB TransactWrite: 最大100アイテム、同一リージョン内のみ
dynamodb.transact_write(
TransactItems=[
{'Put': {'TableName': 'Orders', 'Item': order}},
{'Update': {'TableName': 'Inventory', 'Key': ...}},
]
)
# 異なるリージョンをまたぐトランザクションは不可
なぜ重要か / いつ使うか¶
- AWSでスケーラブルなサービスを作るとき(DynamoDBは高スケール・低レイテンシが強み)
- RDB設計の経験しかないエンジニアがDynamoDBプロジェクトに入るとき
- GSIの設計、テーブル設計の見直し時
- 「なぜこんなに遅い/高い?」と感じたらScanしていないか確認する