database
A
信頼度ランク
| S | 公式ソース確認済み |
| A | 成功実績多数・失敗例少数 |
| B | 賛否両論 |
| C | 動作未確認・セキュリティリスク高 |
| Z | 個人所感 |
DynamoDBパーティションキー設計 — ホットパーティション・カーディナリティ・アクセスパターン
DynamoDBのパーティションキー設計の原則、ホットパーティション問題の発生条件と対策、アダプティブキャパシティ、Writeシャーディングパターン、GSIを活用した設計を解説。
一言結論
ホットパーティションを防ぐには高カーディナリティのキーを選び、書き込みが集中するケースはシャーディング(乱数サフィックス)で分散させることが基本戦略であり、アダプティブキャパシティはあくまで緩和策であって根本的な設計問題の解決にはならない。
DynamoDBのデータ分散の仕組み
DynamoDBはパーティションキーのハッシュ値によってデータを複数のパーティションに分散する。
データ → パーティションキーのハッシュ計算 → パーティション選択 → ストレージ
1パーティションの容量: 10GB
1パーティションのスループット上限: 3,000 RCU / 1,000 WCU
ホットパーティション問題
アクセスが特定のパーティションキーに集中すると、そのパーティションのスループット上限に達し、ProvisionedThroughputExceededException が発生する。
❌ 悪いパーティションキーの例:
status(active/inactive の2値)
→ 全レコードが2パーティションに集中
date(YYYY-MM-DD形式)
→ 当日のアクセスが今日のパーティションに集中(ホット日付)
user_type(admin/member/guest の3値)
→ ユーザータイプに偏りがある場合にホットパーティション
✅ 良いパーティションキーの例:
userId(UUID, 高カーディナリティ)
→ ユーザーごとに分散
deviceId(IoTデバイスID)
→ デバイスごとに分散
orderId(自動生成UUID)
→ 注文ごとに分散
アダプティブキャパシティ
DynamoDBは自動的にホットパーティションを検出し、一時的にキャパシティを増やす機能(Adaptive Capacity)を持つ。ただしこれは緩和策であり、根本的な設計解決ではない。
On-demand モード:
→ 需要に応じて自動スケール(アダプティブキャパシティに加えて)
→ 突発的なトラフィックに対応
Provisioned モード + Auto Scaling:
→ CloudWatchメトリクスに基づいて自動調整
→ アダプティブキャパシティが補助
ホットパーティション対策: シャーディング(Suffix追加)
書き込みが集中するキーに乱数サフィックスを追加して分散する。
import random
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('GameLeaderboard')
# シャード数(書き込みのホットパーティション対策)
SHARD_COUNT = 10
def write_score(user_id: str, score: int):
# パーティションキーにシャードサフィックスを追加
shard = random.randint(0, SHARD_COUNT - 1)
sharded_key = f"{user_id}_shard{shard}"
table.put_item(Item={
'pk': sharded_key,
'score': score,
'timestamp': int(time.time())
})
def read_user_scores(user_id: str):
# 全シャードから読み取って集計
scores = []
for shard in range(SHARD_COUNT):
sharded_key = f"{user_id}_shard{shard}"
response = table.get_item(Key={'pk': sharded_key})
if 'Item' in response:
scores.append(response['Item'])
return scores
複合主キーの設計
パーティションキー(PK)とソートキー(SK)の組み合わせでデータモデルを設計する。
# 1テーブルデザイン(Single Table Design)
# ユーザー情報、注文、注文アイテムを1テーブルに
# ユーザー情報
{
"PK": "USER#user123",
"SK": "PROFILE",
"name": "田中太郎",
"email": "tanaka@example.com"
}
# ユーザーの注文
{
"PK": "USER#user123",
"SK": "ORDER#2026-04-08#order456",
"total": 5000,
"status": "completed"
}
# 注文アイテム
{
"PK": "ORDER#order456",
"SK": "ITEM#item789",
"quantity": 2,
"price": 2500
}
このパターンでは:
PK=USER#user123かつSK begins_with ORDERでユーザーの注文一覧を取得PK=ORDER#order456かつSK begins_with ITEMで注文アイテムを取得
時系列データのキー設計
# ❌ 悪い例: 日付をパーティションキーに使う
{
"date": "2026-04-08", ← ホットパーティション
"timestamp": 1712534400,
"event_type": "click"
}
# ✅ 良い例: 高カーディナリティのIDをPKにして時刻をSKに
{
"deviceId": "device-uuid-xyz", ← 高カーディナリティ
"timestamp": 1712534400, ← ソートキーで時系列順
"event_type": "click"
}
# クエリ例: 特定デバイスの時系列データを取得
table.query(
KeyConditionExpression=Key('deviceId').eq('device-uuid-xyz') &
Key('timestamp').between(start_time, end_time)
)
DynamoDB Streams + Lambdaでの集計
ホットパーティションを避けつつリアルタイム集計をする場合、書き込みを分散してStreamで集計する。
# DynamoDB Streamsで変更を検知して集計テーブルに反映
def handler(event, context):
for record in event['Records']:
if record['eventName'] == 'INSERT':
new_item = record['dynamodb']['NewImage']
# 集計ロジック
update_aggregation(new_item)
試験頻出ポイント
| シナリオ | 回答 |
|---|---|
| ProvisionedThroughputExceededException が頻発 | ホットパーティション問題、PKの見直し |
| On-demandとProvisioned、どちらを使うか | 予測不能なトラフィックはOn-demand |
| 時系列データで日付をPKにした場合 | ホットパーティション問題が発生 |
| 特定ユーザーへのアクセス集中対策 | シャーディング(乱数サフィックス) |
| DynamoDBの1パーティションあたりのWCU上限 | 1,000 WCU |
まとめ
DynamoDBのパフォーマンスはパーティションキーの設計で決まる。高カーディナリティのキーを選択し、書き込みが集中するケースではシャーディングで分散する。On-demandモードはホットパーティションのアダプティブキャパシティが有効で、予測不能なワークロードに適している。