発展LESSON 04
この教材で学ぶこと DynamoDB・RedisのモデルとRDBとの使い分けを学びます。NoSQL — DynamoDB・Redis・使い分け
前提知識: RDBの基本。本教材ではNoSQL固有の考え方と設計パターンを扱います。
1. RDB vs NoSQL の選択基準
RDB(PostgreSQL/Aurora)を選ぶとき:
✅ 複雑な JOIN・集計が必要
✅ ACID トランザクションが必須(金融・在庫)
✅ スキーマが安定している
✅ チームに SQL 知識がある
DynamoDB を選ぶとき:
✅ アクセスパターンが明確(事前に決まっている)
✅ 超高スループット(1秒間に数十万リクエスト)
✅ グローバルテーブル(マルチリージョン)が必要
✅ サーバーレス構成(Lambda との相性抜群)
Redis を選ぶとき:
✅ セッション・キャッシュ(ミリ秒以下のレスポンスが必要)
✅ リーダーボード(ZSET)
✅ Pub/Sub(チャット・リアルタイム通知)
✅ 分散ロック
2. DynamoDB — シングルテーブル設計
RDB の思想: テーブルを正規化して JOIN で結合
DynamoDB の思想: 1テーブルに全エンティティを入れてアクセスパターンに最適化
シングルテーブル設計の利点:
- 1リクエストで関連データを全部取得(JOIN 不要)
- RCU/WCU のコントロールが楽
- Hot Partition を避けやすい
設計手順:
1. アクセスパターンを全て列挙する
2. PK/SK の命名規則を設計する
3. GSI で補助インデックスを追加する
設計例(ECサイト)
エンティティ: User・Order・OrderItem・Product
アクセスパターン:
1. ユーザー情報を取得 (userId)
2. ユーザーの注文一覧を取得(新しい順)
3. 注文の詳細を取得(orderItems含む)
4. ステータス別の注文一覧
テーブル設計:
PK SK Type
USER#user001 #METADATA User
USER#user001 ORDER#2024-01-15 Order
ORDER#order001 ITEM#item001 OrderItem
PRODUCT#prod001 #METADATA Product
GSI1 (ステータス別注文):
GSI1PK: STATUS#shipped
GSI1SK: 2024-01-15
import boto3
from boto3.dynamodb.conditions import Key
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("ECommerce")
# 1. ユーザー情報取得
user = table.get_item(
Key={"PK": "USER#user001", "SK": "#METADATA"}
)["Item"]
# 2. ユーザーの注文一覧(新しい順)
orders = table.query(
KeyConditionExpression=Key("PK").eq("USER#user001") &
Key("SK").begins_with("ORDER#"),
ScanIndexForward=False
)["Items"]
# 3. 注文の詳細とアイテムを一括取得
# PK = ORDER#order001 のものを全て取得
result = table.query(
KeyConditionExpression=Key("PK").eq("ORDER#order001")
)["Items"]
# → 注文自体 + 全アイテムが1クエリで取れる
# 4. ステータス別の注文(GSI 使用)
shipped = table.query(
IndexName="GSI1",
KeyConditionExpression=Key("GSI1PK").eq("STATUS#shipped")
)["Items"]
3. DynamoDB のキャパシティモード
プロビジョニング済みキャパシティ:
RCU(読み取り)と WCU(書き込み)を事前に設定
→ アクセスパターンが予測可能な場合
→ Reserved Capacity で最大65%削減
1 RCU = 強力な一貫性読み取り 1回(4KB以内)or 結果整合性読み取り 2回
1 WCU = 書き込み 1回(1KB以内)
オンデマンド:
自動でスケール、使った分だけ課金
→ 新規サービス・突発的なスパイク
→ プロビジョニング済みより3〜5倍高コスト
Auto Scaling(プロビジョニング済みに追加):
CPU 使用率に応じてRCU/WCUを自動調整
→ プロビジョニング + 柔軟性の両立
4. Redis — データ構造別の活用
import redis
r = redis.Redis(host="localhost", port=6379, decode_responses=True)
# ===== String =====
# セッション・API レスポンスキャッシュ
r.setex("session:token123", 3600, "user001") # TTL 1時間
r.get("session:token123")
# カウンター(原子的操作)
r.incr("page_views:home")
# ===== Hash =====
# ユーザーデータのキャッシュ(フィールド別にアクセス可能)
r.hset("user:001", mapping={
"name": "田中太郎",
"email": "tanaka@example.com",
"role": "admin"
})
r.hget("user:001", "name") # 特定フィールドのみ取得
r.hgetall("user:001") # 全フィールド取得
# ===== Sorted Set =====
# リーダーボード(スコア付きで自動ソート)
r.zadd("leaderboard:2024-01", {"user001": 1500, "user002": 2300, "user003": 800})
r.zrevrange("leaderboard:2024-01", 0, 9, withscores=True) # TOP10
# ===== List =====
# タスクキュー(簡易版; 本番は SQS/Celery を推奨)
r.lpush("task_queue", json.dumps({"type": "send_email", "to": "user@example.com"}))
task = r.brpop("task_queue", timeout=30) # ブロッキング取得
# ===== Pub/Sub =====
# リアルタイム通知(チャット・WebSocket 連携)
pubsub = r.pubsub()
pubsub.subscribe("notifications:user001")
for message in pubsub.listen():
if message["type"] == "message":
print(message["data"])
5. Redis の永続化とレプリケーション
永続化の選択:
RDB(スナップショット):
定期的にダンプファイルを作成
リカバリが遅い・データ損失の可能性あり
AOF(Append Only File):
全書き込みをログに記録
リカバリが確実・ファイルが大きくなる
推奨: AOF + everysec(1秒ごとにフラッシュ)
ElastiCache Redis: AOF は無効(フェイルオーバーでレプリカが昇格)
ElastiCache の構成:
Cluster Mode OFF: Primary 1 + Read Replica 最大5
Cluster Mode ON: シャーディング(データを複数ノードに分散)
スケールアウトが必要な大規模向け
6. DynamoDB vs Redis の棲み分け
同じ「高速な読み書き」でも違う:
DynamoDB:
✅ 永続化(データが消えない)
✅ 無制限のサイズ
✅ サーバーレス(管理不要)
❌ ミリ秒単位のレスポンス(数ms〜10ms程度)
Redis:
✅ サブミリ秒のレスポンス
✅ 豊富なデータ構造(Sorted Set 等)
✅ Pub/Sub
❌ メモリに全データを載せる(コスト大)
❌ 揮発性(設定によって消える)
組み合わせパターン:
DynamoDB(永続化)+ Redis(キャッシュ)
DynamoDB → Write-Through で Redis に書く
→ 読み取りは Redis から(速い)
→ データは DynamoDB に永続化(安全)
🔗 次のステップ
- 02_RDB設計パターン.md — PostgreSQL のインデックス・最適化
- AWSデータベースサービス — DynamoDB/ElastiCache の AWS 設計