- 1 1. なぜDatabase Vol3 (Cache編) か — Database三部作完結 + キャッシュ戦略の現実解
- 2 2. ElastiCache for Redis 本番運用 — Cluster Mode × Multi-AZ × TLS × Backup
- 3 3. ElastiCache for Memcached 本番運用 — Node Type × Auto Discovery × Redis比較
- 4 4. DAX (DynamoDB Accelerator) 本番運用 — Item Cache × Query Cache × Write-Through
- 5 5. MemoryDB for Redis 本番運用 — Durable Redis × Multi-Region × Snapshot
- 6 6. キャッシュ本番運用 詰まりポイント7選
- 7 7. アンチパターン → 正解パターン変換演習 5問
- 8 8. まとめ — Database三部作完結 + 全17軸クロスリンク
1. なぜDatabase Vol3 (Cache編) か — Database三部作完結 + キャッシュ戦略の現実解
- 前作Vol1: Database本番運用 Vol1 — RDS×Aurora×DynamoDB 主データストア完全ガイド
- 前作Vol2: Database本番運用 Vol2 — DMS×Aurora Global Database×Streams×Backup 災対・移行完全ガイド
- 関連 (Storage基盤): Storage本番運用 Vol1 — S3×EFS×FSx×Storage Gateway
- 関連 (AI連携): ML/AI本番運用 Vol2 — Bedrock Embedding×RAG×Knowledge Bases×Agents (Embedding cache統合)
- 関連 (サーバーレス): Serverless本番運用 Vol1 — Lambda×API Gateway×EventBridge
1-1. 本記事のゴール
本記事は AWS のキャッシュサービス3本柱 — ElastiCache for Redis・DAX (DynamoDB Accelerator)・MemoryDB for Redis — を本番運用視点で横断網羅する。各サービスの技術的な違いを整理するだけでなく、「どのユースケースでどれを選ぶか」を決める選定マトリクスと、そのまま本番投入できる Terraform 完全実装例を提供する。
Database Vol1 で主データストア (RDS/Aurora/DynamoDB) を固め、Vol2 で DR・移行・バックアップを固めた。本記事 Vol3 でキャッシュ層まで揃えることで、AWS データ基盤の全層を本番品質で設計・実装できるエンジニアを目指す。
到達点は次の3つだ。
- ElastiCache for Redis を Cluster Mode × Multi-AZ × TLS × RBAC で本番構築できる
- DAX の Item Cache / Query Cache / Batch 動作を理解し、適切なテーブルを選定できる
- MemoryDB for Redis の永続化モデル (Multi-AZ AOF) を理解し、ElastiCache との使い分けができる
1-2. 読者像
本記事の主な読者像は次の通りだ。
- RDS・Aurora・DynamoDB は Vol1 で構築済みで、次はキャッシュ層を本番品質にしたいエンジニア。「とりあえず ElastiCache」で動いているが、Cluster Mode や Multi-AZ の設定に自信がない。
- DAX を DynamoDB の全テーブルに適用してコストが膨らんでいる、または逆に「DAX は使いどころが難しい」と感じていて適用範囲に迷っているエンジニア。
- ElastiCache Redis と MemoryDB for Redis をどう使い分けるか判断できていないエンジニア。永続化・整合性・コストの3軸で迷いやすいポイントだ。
- Cache Stampede やメモリ Eviction といったキャッシュ本番運用の落とし穴を一度経験したことがある、あるいは経験する前に体系的に押さえておきたいエンジニア。
1-3. Vol1/Vol2との架橋 + 三部作完結の意義
- Vol1 (RDS / Aurora / DynamoDB): 主データストア層 — 永続化・整合性・ACID特性を担保する基盤。マルチAZレプリカ、Aurora Global Database の基礎を構築した。
- Vol2 (DMS / Aurora Global / Streams / Backup): 災対と移行層 — クロスリージョン DR・ゼロダウンタイム移行・ポイントインタイムリストアを確立した。
- Vol3 (本記事 — ElastiCache / DAX / MemoryDB): キャッシュ層 — レイテンシ最小化・OriginDB 負荷軽減・コスト最適化を担う。三部作でデータ基盤の全層を網羅する。
- 三部作を組み合わせることで「構築 → 災対 → 高速化」のサイクルが完結し、どんな規模のシステムでも AWS データ基盤を本番品質で運用できるロードマップが手に入る。
- Cache Stampede (Dog-piling) によるOriginDB過負荷: キャッシュ TTL が同時刻に一斉失効すると、数百リクエストが同時に DB へ到達する。対策は Probabilistic Early Expiration (PER) による TTL ゆらぎ追加、または Mutex Lock でリクエストを1本に絞ること。高トラフィック環境では必須対策だが見落とされやすい。
- TTL設計ミスによるデータ陳腐化または過剰更新: TTL を長くしすぎると古いデータがキャッシュに残り続け、ユーザーに誤情報を提供する。短くしすぎるとキャッシュヒット率が低下し OriginDB 負荷が上がる。エンティティの「更新頻度 × 許容遅延」を事前に整理し、TTL ポリシーをデータ種別ごとに設計することが重要だ。
- Single-AZ ElastiCache でのフェイルオーバ失敗: 開発コスト優先で Single-AZ のまま本番投入すると、AZ 障害時にキャッシュが消失し全リクエストが DB に流れ込む。Multi-AZ + Auto-Failover は本番では必須設定。Failover 時間は約 30 秒で DNS TTL を 60 秒以下にする必要もある。
- DAX 全テーブル適用による過剰コスト: DynamoDB の全テーブルに DAX を当てると、書き込み多数・Read 少数のテーブルや管理系テーブルでもコストが発生し続ける。DAX は「読み取り多数・同一キーへのリクエスト集中・ミリ秒以下レイテンシが必要」なテーブルにのみ適用するべきだ。
- MemoryDB vs ElastiCache Redis の選定誤り: “Redis が使いたい” だけで MemoryDB を選ぶと、ElastiCache Redis の 2〜3 倍のコストが発生する。MemoryDB が必要なのは「キャッシュでなくプライマリデータストアとして Redis を使う」「AOF による全データ永続化が必須」「強整合性 (Multi-AZ Transactional Log) が要件」の場合だ。セッション管理やリアルタイムカウンタなら ElastiCache Redis で十分なことが多い。
1-4. キャッシュ層を後回しにするリスク
キャッシュ層の構築を後回しにすることには、見えにくいリスクが3軸で存在する。
レイテンシ軸: RDS や DynamoDB へのクエリはネットワークレイテンシ込みで数ミリ秒〜数十ミリ秒かかる。ElastiCache Redis のインメモリアクセスは 0.1〜1 ミリ秒台だ。ユーザー体感速度はページ表示時間に直結し、EC サイトでは 100ms の遅延で直帰率が数 % 上昇するという A/B テスト結果が複数報告されている。
コスト軸: DB クエリは RCU / CPU / IOPS コストを消費する。DAU 10 万のサービスでセッションデータや商品マスタを毎回 RDS から取得した場合、月間のクエリ課金は ElastiCache Reserved Node の数倍に達することがある。キャッシュは「追加コスト」でなく「DB コスト削減手段」として捉えるべきだ。
スケール軸: トラフィックが増加した際に DB だけスケールアップし続けるアーキテクチャは限界が早い。ElastiCache Cluster Mode のシャード追加はダウンタイムゼロで水平スケールが可能だ。キャッシュ層を設計に組み込むことで、DB のスケール限界を大幅に先延ばしできる。

2. ElastiCache for Redis 本番運用 — Cluster Mode × Multi-AZ × TLS × Backup
2-1. Cluster Mode設計 — シャーディング + ノード数計算
ElastiCache Redis には Cluster Mode Enabled と Cluster Mode Disabled (Replication Group) の2つのモードがある。本番ワークロードに合わせた選定基準を以下の表で整理する。
| 項目 | Cluster Mode Enabled | Cluster Mode Disabled |
|---|---|---|
| シャーディング | ◎ 複数シャードで水平分割 | ✕ 単一シャード |
| スケールアウト | ◎ シャード追加でゼロダウンタイム拡張 | ✕ 不可 (スケールアップのみ) |
| メモリ上限 | ◎ シャード数 × ノードメモリ | △ 単ノードのメモリ上限 |
| Failover | ◎ シャード単位 Auto-Failover | ◎ Replication Group 単位 |
| マルチキー操作 | △ 同一スロットのキーのみ MGET/MSET 可 | ◎ 制限なし |
| Lua スクリプト | △ クロススロット参照不可 | ◎ 制限なし |
| 推奨用途 | 大規模/高スループット/水平スケール必要 | 小〜中規模/Lua多用/マルチキー多用 |
シャード数計算式:
シャード数 = CEIL(総データ量 [GB] / ノードメモリ [GB] × 1.5)
係数 1.5 はメモリオーバーヘッド (Redis 内部データ構造・レプリケーションバッファ) を考慮した安全マージンだ。例えば総データ量 60 GB / ノードメモリ (cache.r7g.xlarge: 26.32 GB) の場合:
CEIL(60 / 26.32 × 1.5) = CEIL(3.42) = 4 シャード
最小本番構成例 (cache.r7g.large: 13.07 GB):
- 3 シャード × Primary 1 + Replica 2 = 9 ノード合計
- 各シャードが別 AZ (ap-northeast-1a / 1c / 1d) に Primary を配置
- Replica は Primary と別 AZ に自動配置 (Auto-Failover 有効時)
Cluster Mode のシャーディングは CRC16 ハッシュスロットで行われる。キーの CRC16 値を 16384 で割った余りをスロット番号とし、各シャードにスロット範囲を割り当てる。クライアントライブラリ (redis-py-cluster / Jedis Cluster 等) がスロットマップを自動管理し、正しいノードにルーティングする。

2-2. Replication Group — Multi-AZ × Auto-Failover
本番では Primary 1 台 + Replica 2 台の最小3ノード構成が基本だ。
ap-northeast-1a: Primary node
ap-northeast-1c: Replica-1
ap-northeast-1d: Replica-2
Failover 動作の注意点:
- Primary 障害検知から DNS フリップ完了まで 約 30 秒。アプリ側には接続リトライ (指数バックオフ) を必ず実装する。
- ElastiCache のエンドポイントは CNAME で提供される。DNS TTL = 60 秒のため、クライアントが古い IP をキャッシュしていると Failover 後も旧 Primary に接続し続ける恐れがある。JVM や Go の DNS キャッシュ設定を確認し、TTL = 5〜10 秒程度に短縮することを推奨する。
Read 負荷分散:
Cluster Mode Disabled の場合は Reader Endpoint が提供される。全 Replica にラウンドロビンでアクセスできるため、Read 重視のワークロードでは Primary の CPU 負荷を大幅に下げられる。Cluster Mode Enabled の場合は各シャードの Replica に接続するよう、クライアントの readonly コマンドを使用する。
Automatic Failover vs Manual Failover:
- Automatic Failover: AZ 障害・ノード障害を ElastiCache が自動検知し Primary を昇格。Multi-AZ 有効時は常に ON にすること。
- Manual Failover (TestFailover API): メンテナンス計画・DR 訓練で意図的にフェイルオーバをトリガーする際に使用。本番で月1回のフェイルオーバ訓練を推奨する。
2-3. TLS in-transit + RBAC認証
TLS 設定: transit_encryption_enabled = true を必須とする。ElastiCache は AWS 発行の TLS 証明書を使用し、クライアントは tls:// スキームで接続する。ElastiCache Serverless でも同設定が適用される。
RBAC (Role-Based Access Control) 設定手順:
- ElastiCache User を作成し、コマンド権限を設定する
- ElastiCache User Group を作成し、User を紐付ける
- Replication Group に User Group を関連付ける
権限設定例:
– ~* + +@all: 全キー・全コマンドへのフルアクセス (管理者用)
– ~readonly:* + +@read: readonly: プレフィックスのキーに Read のみ
– ~session:* + +@write +@read -@dangerous: セッション管理用ユーザー
IAM Auth Token: ElastiCache IAM 認証を使うと EC2 / Lambda の IAM ロールを使って認証できる。パスワード管理が不要になりセキュリティ向上につながる。対象は ElastiCache Serverless + ElastiCache for Redis 7.0 以降。
AWS CLI でのユーザー / ユーザーグループ作成コマンドは後述するが、Terraform での一括管理が保守性に優れる。
Terraform による TLS + RBAC 完全実装例:
resource "aws_elasticache_subnet_group" "redis" {
name = "redis-subnet-group"
subnet_ids = var.private_subnet_ids
}
resource "aws_elasticache_parameter_group" "redis7" {
name= "redis7-production"
family = "redis7"
parameter {
name = "maxmemory-policy"
value = "allkeys-lru"
}
parameter {
name = "lazyfree-lazy-eviction"
value = "yes"
}
parameter {
name = "timeout"
value = "300"
}
parameter {
name = "tcp-keepalive"
value = "300"
}
}
resource "aws_elasticache_user" "app_readonly" {
user_id = "app-readonly"
user_name = "app-readonly"
access_string = "on ~readonly:* +@read"
engine = "REDIS"
passwords = [var.redis_readonly_password]
}
resource "aws_elasticache_user" "app_readwrite" {
user_id = "app-readwrite"
user_name = "app-readwrite"
access_string = "on ~* +@all -@dangerous"
engine = "REDIS"
passwords = [var.redis_readwrite_password]
}
resource "aws_elasticache_user_group" "app" {
engine = "REDIS"
user_group_id = "app-user-group"
user_ids= [aws_elasticache_user.app_readonly.user_id, aws_elasticache_user.app_readwrite.user_id]
}
resource "aws_elasticache_replication_group" "redis" {
replication_group_id = "production-redis"
description = "Production Redis Cluster Mode Enabled"
engine= "redis"
engine_version = "7.0"
node_type= "cache.r7g.large"
parameter_group_name = aws_elasticache_parameter_group.redis7.name
subnet_group_name = aws_elasticache_subnet_group.redis.name
security_group_ids= [aws_security_group.redis.id]
# Cluster Mode Enabled
num_node_groups= 3
replicas_per_node_group = 2
# Multi-AZ + Auto-Failover
multi_az_enabled = true
automatic_failover_enabled = true
# TLS
transit_encryption_enabled = true
at_rest_encryption_enabled = true
# RBAC
user_group_ids = [aws_elasticache_user_group.app.user_group_id]
# Backup
snapshot_retention_limit = 7
snapshot_window = "17:00-18:00"
maintenance_window = "sun:18:00-sun:19:00"
# CloudWatch Logs
log_delivery_configuration {
destination= aws_cloudwatch_log_group.redis_slow_log.name
destination_type = "cloudwatch-logs"
log_format = "json"
log_type= "slow-log"
}
tags = {
Environment = "production"
Terraform= "true"
}
}
2-4. Backup — AOF + RDB + S3エクスポート
自動スナップショット: snapshot_retention_limit = 7 が本番設定の基本だ。これで7日間のスナップショットが自動保持される。snapshot_window は低トラフィック時間帯 (例: 17:00-18:00 UTC = 深夜2時 JST) に設定する。
手動スナップショット: メンテナンス作業・エンジンバージョンアップ前には必ず手動スナップショットを取得する。自動スナップショットの保持期間に依存せず、任意の期間保持できる。
aws elasticache create-snapshot \
--replication-group-id production-redis \
--snapshot-name pre-maintenance-20260517
S3 エクスポート (クロスリージョン DR 用):
# Step 1: スナップショット作成
aws elasticache create-snapshot \
--replication-group-id production-redis \
--snapshot-name dr-export-20260517
# Step 2: S3 に RDB ファイルをエクスポート
aws elasticache copy-snapshot \
--source-snapshot-name dr-export-20260517 \
--target-snapshot-name dr-export-20260517-s3 \
--target-bucket "s3://your-dr-bucket/redis-backups/"
エクスポートされた RDB ファイルは DR リージョンで aws elasticache restore-replication-group-from-s3 コマンドでリストア可能だ。
AOF (Append Only File) と RDB の違い:
| 方式 | 説明 | ElastiCache での扱い |
|---|---|---|
| RDB | 定期的なポイントインタイムスナップショット | ElastiCache スナップショット = RDB ベース |
| AOF | 全書き込み操作のログ | ElastiCache ではクラスター内レプリケーションに相当 |
ElastiCache のスナップショットは RDB 形式だ。AOF の永続化 (全操作の永続ログ) が必要なケースでは MemoryDB for Redis を選択する (§4 で解説)。
2-5. パラメータチューニング
maxmemory-policy 比較表:
| ポリシー | 動作 | 推奨シナリオ |
|---|---|---|
allkeys-lru | 全キーからLRUで削除 | セッション・汎用キャッシュ (キーに TTL なし混在) |
volatile-lru | TTL 付きキーのみ LRU で削除 | TTL で期限管理し、永続キーを保持したい場合 |
allkeys-lfu | 全キーからアクセス頻度最小順で削除 | アクセス偏りが大きいホットキーが存在する場合 |
volatile-lfu | TTL 付きキーのみ LFU で削除 | TTL 管理 + アクセス頻度重視の混在環境 |
noeviction | メモリ満杯時にエラー返却 | データ消失が許容できないプライマリストア用途 |
allkeys-random | 全キーからランダム削除 | アクセスパターンがほぼ均一な場合 |
本番では allkeys-lru を基本とし、DynamoDB や RDS のキャッシュ層として使う場合は volatile-lru も検討する。
主要パラメータ:
lazyfree-lazy-eviction: yes # 非同期削除でメインスレッドのレイテンシ改善
lazyfree-lazy-expire:yes # TTL 失効キーの非同期削除
timeout: 300 # アイドル接続を 300 秒で切断
tcp-keepalive: 300 # Keep-Alive パケット間隔 (秒)
CloudWatch Metrics 監視設定とアラーム閾値:
| メトリクス | 閾値 (本番基準) | 意味 |
|---|---|---|
Evictions | > 0 で即 Alert | キャッシュがメモリ不足でデータを追い出している |
SwapUsage | > 50 MB で Warning, > 100 MB で Alert | スワップ発生はレイテンシ急増の前兆 |
CPUUtilization | > 70% で Warning | Redis はシングルスレッドのため上限が低い |
CurrConnections | 異常増加で Warning | コネクションリークの検知 |
ReplicationLag | > 100 ms で Warning | レプリカの遅延 (Failover 時のデータ欠損リスク) |
DatabaseMemoryUsagePercentage | > 80% で Warning | メモリ使用率の早期警告 |
Evictions > 0 は「今すぐスケールアウトが必要」を意味するため、即時 Alert に設定する。
2-6. スケール戦略 — Out vs Up 判断フロー
スケールアウト (シャード追加):
Cluster Mode Enabled でのみ実行可能。シャード追加はオンラインで行われ、ダウンタイムなしでデータが再分散される。
トリガー: Evictions > 0 が持続 (メモリ不足)
操作: ElastiCache コンソール or Terraform で num_node_groups を増加
所要時間: 数分〜15分 (データ再分散)
スケールアップ (ノードタイプ昇格):
トリガー: CPUUtilization > 70% が持続 (CPU ボトルネック)
操作: node_type を変更 (例: cache.r7g.large → cache.r7g.xlarge)
所要時間: Rolling Update で約 30〜60 分 (Primary → Replica の順)
判断フロー:
① CPUUtilization > 70% 持続
→ コマンド処理量超過 → スケールアップ (ノードタイプ昇格)
② Evictions > 0 持続 + DatabaseMemoryUsagePercentage > 80%
→ メモリ不足 → スケールアウト (シャード追加) [Cluster Mode Enabled 前提]
→ Cluster Mode Disabled の場合は スケールアップ or Cluster Mode Enabled に移行
③ ReplicationLag > 100ms 持続
→ レプリカ書き込み追従不足 → レプリカノードタイプ昇格
④ CurrConnections 上限付近
→ アプリ側コネクションプール設定の見直し (ElastiCache 単体のスケールでは解決しない)
Data Tiering (cache.r6gd): SSD と DRAM を階層化するノードタイプだ。アクセス頻度の低いデータを自動的に SSD に移動し、メモリコストを削減できる。大規模キャッシュ (数 TB 規模) でコスト最適化が必要な場合に有効だ。ただしレイテンシは DRAM アクセスより増加するため、ホットデータ比率と許容レイテンシを事前に確認する。
AWS CLI — ElastiCache User / UserGroup 作成コマンド:
# ユーザー作成 (ReadWrite)
aws elasticache create-user \
--user-id app-readwrite \
--user-name app-readwrite \
--engine REDIS \
--access-string "on ~* +@all -@dangerous" \
--passwords "YourStrongPassword123!"
# ユーザー作成 (ReadOnly)
aws elasticache create-user \
--user-id app-readonly \
--user-name app-readonly \
--engine REDIS \
--access-string "on ~readonly:* +@read" \
--passwords "AnotherStrongPassword456!"
# ユーザーグループ作成
aws elasticache create-user-group \
--user-group-id app-user-group \
--engine REDIS \
--user-ids app-readwrite app-readonly
# Replication Group に UserGroup 関連付け
aws elasticache modify-replication-group \
--replication-group-id production-redis \
--user-group-ids-to-add app-user-group \
--apply-immediately
- Multi-AZ + Auto-Failover を本番デフォルト: Cluster Mode Enabled なら num_node_groups ≥ 3 / replicas_per_node_group ≥ 1 を最小構成とする。AZ をまたいだ Primary 配置で単一 AZ 障害を吸収する。
- TLS + RBAC をセットで設定: transit_encryption_enabled = true と user_group_ids の両方を設定しないと、暗号化は通信レベルの保護のみになる。アプリケーション別にユーザーと権限を分離することで、万が一の認証情報漏洩時の影響範囲を最小化できる。
- snapshot_retention_limit = 7 + Manual Snapshot 運用: 自動スナップショット7日分に加え、メンテナンス前後に手動スナップショットを取得する。S3 エクスポートで DR リージョンにバックアップを持ち出す手順を事前に確立しておく。
- CloudWatch Alarm の必須設定: Evictions > 0 (即 Alert)・SwapUsage > 50 MB (Warning) の2つは本番必須。これらを見逃すとアプリケーション障害が表面化してから原因特定に時間がかかる。
- スロークエリログを CloudWatch Logs に転送: log_delivery_configuration で slow-log を CloudWatch Logs に送り、O(N) コマンド (KEYS・HGETALL 等) の使用を早期に検出する。
- Reserved Node Pricing (1年/3年予約): On-Demand 比で 1 年予約は約 30% 削減、3 年予約は約 55% 削減になる。本番環境で常時稼働するクラスターは Reserved Node に切り替えることでランニングコストを大幅削減できる。Convertible Reserved は途中でノードタイプ変更が可能なため柔軟性が高い。
- Data Tiering (cache.r6gd) でメモリ階層化: 総キャッシュデータ量が数 TB を超える場合、DRAM のみの r7g 系より cache.r6gd (DRAM + NVMe SSD) でコストを 30〜50% 削減できる。ホットデータは DRAM、コールドデータは SSD に自動階層化される。
- 非本番環境のサイズダウン: 開発・ステージング環境は cache.t4g.micro〜small で十分なケースが多い。Reserved Node は本番のみに適用し、非本番は On-Demand + 小サイズで維持することが最もコスト効率が高い。
- ElastiCache Serverless の活用: 開発環境や低トラフィックのバッチ処理用途では ElastiCache Serverless が有効。使用した ECU (ElastiCache Compute Units) と GB の分だけ課金されるため、アイドル時間の長いワークロードには On-Demand より安くなる。
3. ElastiCache for Memcached 本番運用 — Node Type × Auto Discovery × Redis比較
ElastiCache for Memcached はマルチスレッドアーキテクチャを活用した高スループットキャッシュサービスだ。Redis と比較してシンプルなデータモデルながら、水平スケールと並列処理能力に優れており、純粋なキャッシュ用途に最適化されている。
3-1. Node Type選定 — cache.r7g vs cache.m7g
Memcached ノードタイプ選定は「メモリ容量優先」か「コスト最適化」かで分岐する。
メモリ重視: cache.r7g シリーズ (Graviton3 Memory-Optimized)
| ノードタイプ | メモリ | vCPU | 価格 (東京) | 推奨用途 |
|---|---|---|---|---|
| cache.r7g.large | 6.38 GB | 2 | $0.192/h | セッションキャッシュ、オブジェクトキャッシュ |
| cache.r7g.xlarge | 12.93 GB | 4 | $0.384/h | 中規模アプリケーション |
| cache.r7g.2xlarge | 26.04 GB | 8 | $0.768/h | 大規模オブジェクトキャッシュ |
| cache.r7g.4xlarge | 52.24 GB | 16 | $1.536/h | エンタープライズ用途 |
バランス重視: cache.m7g シリーズ (Graviton3 General-Purpose)
| ノードタイプ | メモリ | vCPU | 価格 (東京) | 推奨用途 |
|---|---|---|---|---|
| cache.m7g.large | 3.09 GB | 2 | $0.133/h | 低コスト汎用キャッシュ |
| cache.m7g.xlarge | 6.38 GB | 4 | $0.266/h | 中規模汎用キャッシュ |
| cache.m7g.2xlarge | 12.93 GB | 8 | $0.532/h | 高スループット汎用 |
Graviton3採用によるコスト効率改善
Graviton3 (r7g/m7g) は従来の Intel/AMD ベース (r6g/m6g) と比較して約 20% のコスト効率改善を実現する。同一メモリ容量でスループットが向上するため、新規構築では Graviton3 系を選定すること。
ノード数計算式
# 必要ノード数の計算
# 必要ノード数 = ceil(総キャッシュデータ量 / ノードメモリ × 1.5)
# 安全係数 1.5: キーオーバーヘッド10% + 増加バッファ20% + 内部管理領域20%
例: 総キャッシュデータ量 30 GB、cache.r7g.large (6.38 GB) 使用時:
– 必要ノード数 = ceil(30 / 6.38 × 1.5) = ceil(7.05) = 8ノード
3-2. Auto Discovery — ElastiCache クラスターエンドポイント
Auto Discovery は ElastiCache for Memcached クラスターのノード情報をアプリケーション側で自動取得する機能だ。ノードの追加・削除が発生しても、アプリケーション側の設定変更なしに接続先を更新できる。
仕組み
Configuration Endpoint (mycluster.cfg.xxx.cache.amazonaws.com:11211) に接続すると、クラスター内の全ノード IP・ポートが返却される。Auto Discovery 対応クライアントはデフォルト 60 秒間隔でポーリングし、ノード追加・削除を自動検知して接続プールを更新する。
Auto Discovery 対応 SDK
| 言語 | SDK | 入手先 |
|---|---|---|
| Java | Amazon ElastiCache Cluster Client for Java | AWS GitHub |
| PHP | Amazon ElastiCache Cluster Client for PHP | AWS GitHub |
| Python | pymemcache + ElastiCache Discovery | pip install |
| .NET | EnyimMemcached + AutoDiscovery Extension | NuGet |
Python 設定例
from pymemcache.client.hash import HashClient
cluster_endpoint = "mycluster.cfg.xxx.cache.amazonaws.com"
cluster_port = 11211
client = HashClient(
[(cluster_endpoint, cluster_port)],
use_pooling=True,
max_pool_size=10,
timeout=0.5,
connect_timeout=1.0,
)
client.set("session:user123", "session_data", expire=3600)
value = client.get("session:user123")
Auto Discovery 非対応言語での代替策
Node.js や Ruby など一部の言語・ライブラリは Auto Discovery に非対応だ。この場合は以下の代替策を検討する:
- 個別ノードエンドポイントをハードコード — スケール時に手動更新が必要 (運用負荷大)
- Redis への移行 — ElastiCache for Redis はクラスターモード対応でノード自動検出が容易
- サービスメッシュ経由 — ECS Service Connect でエンドポイント管理を委譲
一般的に Auto Discovery 非対応言語での Memcached 運用は推奨しない。Redis への移行を優先すること。
3-3. Redis vs Memcached 選定マトリクス
| 比較項目 | Redis | Memcached |
|---|---|---|
| データ永続化 | RDB + AOF 対応 | なし (揮発性のみ) |
| Multi-AZ / Failover | あり (自動フェイルオーバー) | なし |
| データ型 | 豊富 (String/List/Set/Hash/Sorted Set/Stream) | シンプル (文字列のみ) |
| スレッドモデル | シングルスレッド (I/O多重化) | マルチスレッド |
| 最大スループット | 単一コア上限あり | マルチコア活用可 |
| Pub/Sub | あり | なし |
| Lua scripting | あり | なし |
| Sorted Set / Leaderboard | あり | なし |
| Transactions (MULTI/EXEC) | あり | なし |
| Auto Discovery | クラスターモードで対応 | ElastiCache Cluster Client 必要 |
選定判断フロー
キャッシュ要件
├── データ永続化が必要? → YES → Redis
├── Multi-AZ フェイルオーバーが必要? → YES → Redis
├── Pub/Sub が必要? → YES → Redis
├── Sorted Set / Leaderboard が必要? → YES → Redis
├── Lua スクリプトが必要? → YES → Redis
├── Auto Discovery 非対応言語を使用? → YES → Redis
└── 上記すべて NO かつ高並列マルチスレッドが必要? → Memcached
現代のほとんどのユースケースでは Redis が推奨される。Memcached を選ぶ積極的な理由は「マルチスレッドによる高並列処理」かつ「データ永続化不要」の場合のみだ。特に数百万 QPS レベルの純粋なキー/バリューキャッシュや、小さなオブジェクト (128 bytes 以下) の大量キャッシュでは Memcached の優位性がある。
- Redisを選ぶ場合: Multi-AZ Failover / Pub/Sub / Sorted Set / Lua / データ永続化 / TLS+RBAC のいずれかが必要
- Memcachedを選ぶ場合: 純粋なKey/Valueキャッシュのみ + マルチスレッドによる高並列処理が必要 + データ永続化不要
- 迷ったらRedis: 機能の超過コストはほぼゼロ。将来の要件変化にも対応可能
3-4. ユースケース — セッションキャッシュ / フルページキャッシュ
PHP セッションキャッシュ設定例
# php.ini での ElastiCache Memcached セッション設定
# php-memcached 拡張 (libmemcached ベース) を使用
session.save_handler = memcached
session.save_path = "mycluster.cfg.xxx.cache.amazonaws.com:11211?persistent=1&weight=1&timeout=1&retry_interval=15"
php-memcache (古い拡張) ではなく php-memcached を使うこと。PECL install memcached でインストール後、extension=memcached.so を有効化する。
WordPress オブジェクトキャッシュ (W3 Total Cache)
// wp-content/object-cache.php での設定 (W3 Total Cache が自動生成)
define('W3TC_MEMCACHED_SERVERS', [
'mycluster.cfg.xxx.cache.amazonaws.com:11211',
]);
define('W3TC_MEMCACHED_PERSISTENT', true);
define('W3TC_MEMCACHED_TIMEOUT', 30);
WordPress での設定手順:
1. W3 Total Cache インストール
2. Performance → Object Cache → Memcached を選択
3. Configuration Endpoint を入力
4. wp-content/object-cache.php が自動生成されることを確認
フルページキャッシュ: CloudFront + ElastiCache ハイブリッド構成
ユーザー
↓
CloudFront (CDN層キャッシュ: TTL 5-60分)
↓ キャッシュミス時
Application Load Balancer
↓
EC2 / ECS アプリケーション
↓ DB/API アクセス前にチェック
ElastiCache Memcached (アプリ層キャッシュ: TTL 1-5分)
↓ キャッシュミス時
RDS / DynamoDB
CloudFront: 静的コンテンツ・認証不要ページの長期キャッシュ
ElastiCache: 動的コンテンツ・ユーザー別データの短期キャッシュ
Terraform: aws_elasticache_cluster (Memcached) 完全実装例
resource "aws_elasticache_subnet_group" "memcached" {
name = "memcached-subnet-group"
subnet_ids = var.private_subnet_ids
tags = {
Name = "memcached-subnet-group"
Environment = var.environment
}
}
resource "aws_elasticache_parameter_group" "memcached" {
family = "memcached1.6"
name= "memcached-prod-params"
parameter {
name = "max_item_size"
value = "10485760"
}
parameter {
name = "chunk_size_growth_factor"
value = "1.25"
}
tags = {
Name = "memcached-prod-params"
Environment = var.environment
}
}
resource "aws_security_group" "memcached" {
name = "memcached-sg"
description = "Security group for ElastiCache Memcached cluster"
vpc_id= var.vpc_id
ingress {
description = "Memcached from app tier"
from_port = 11211
to_port= 11211
protocol = "tcp"
security_groups = [aws_security_group.app.id]
}
egress {
from_port= 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "memcached-sg"
Environment = var.environment
}
}
resource "aws_elasticache_cluster" "memcached" {
cluster_id = "memcached-prod"
engine= "memcached"
engine_version = "1.6.22"
node_type= "cache.r7g.large"
num_cache_nodes= 3
parameter_group_name = aws_elasticache_parameter_group.memcached.name
subnet_group_name = aws_elasticache_subnet_group.memcached.name
security_group_ids= [aws_security_group.memcached.id]
port = 11211
maintenance_window= "sun:05:00-sun:06:00"
snapshot_retention_limit = 0
notification_topic_arn= var.sns_topic_arn
az_mode = "cross-az"
preferred_availability_zones = [
"${var.aws_region}a",
"${var.aws_region}c",
"${var.aws_region}d",
]
apply_immediately = false
tags = {
Name = "memcached-prod"
Environment = var.environment
Purpose = "application-cache"
}
}
resource "aws_cloudwatch_metric_alarm" "memcached_cpu" {
alarm_name = "memcached-high-cpu"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name= "CPUUtilization"
namespace = "AWS/ElastiCache"
period = 300
statistic = "Average"
threshold = 80
alarm_description= "ElastiCache Memcached CPU utilization is high"
alarm_actions = [var.sns_topic_arn]
dimensions = {
CacheClusterId = aws_elasticache_cluster.memcached.cluster_id
}
}
resource "aws_cloudwatch_metric_alarm" "memcached_evictions" {
alarm_name = "memcached-high-evictions"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name= "Evictions"
namespace = "AWS/ElastiCache"
period = 300
statistic = "Sum"
threshold = 1000
alarm_description= "High evictions indicate memory pressure on Memcached nodes"
alarm_actions = [var.sns_topic_arn]
dimensions = {
CacheClusterId = aws_elasticache_cluster.memcached.cluster_id
}
}
- データ永続化不要・揮発性キャッシュ専用 → Memcached (永続化・Failover が必要なら Redis)
- マルチスレッドによる高並列処理が必要 (数百万 QPS) → Memcached (Redis はシングルスレッド)
- Auto Discovery 対応 SDK が利用可能な言語 (Java/PHP/Python) → Memcached 運用可。非対応言語では Redis 推奨
- コスト最優先の純粋キャッシュ用途 → cache.m7g.large が最小コスト構成
4. DAX (DynamoDB Accelerator) 本番運用 — Item Cache × Query Cache × Write-Through
DAX は DynamoDB と互換 API を持つマネージドインメモリキャッシュクラスタだ。DynamoDB SDK をほぼ無修正で DAX クライアントに切り替えるだけで、読み取りレイテンシをマイクロ秒単位に短縮できる。
graph LR
subgraph AppLayer["Application Layer"]
App["アプリケーション\nDAX Client"]
end
subgraph DAXCluster["DAX Cluster (3ノード構成)"]
Primary["Primary Node\n書き込み・調整"]
Replica1["Replica Node 1\n読み取り"]
Replica2["Replica Node 2\n読み取り"]
Primary --- Replica1
Primary --- Replica2
end
subgraph CacheStore["Cache Stores"]
IC["Item Cache\nGetItem / BatchGetItem\nTTL: 5分 (デフォルト)"]
QC["Query Cache\nQuery / Scan\nTTL: 1分 (デフォルト)"]
end
subgraph Backend["Backend"]
DDB[("DynamoDB Table")]
end
App -->|"1. 読み取りリクエスト"| Primary
Primary -->|"2a. Cache HIT (μs)"| IC
Primary -->|"2b. Cache HIT (μs)"| QC
IC -->|"3. 即時レスポンス"| App
QC -->|"3. 即時レスポンス"| App
Primary -->|"4. Cache MISS → フォールバック"| DDB
DDB -->|"5. データ返却 + Cache更新"| Primary
Primary -->|"6. レスポンス (ms)"| App
App -->|"Write-Through\n書き込み伝播"| Primary
Primary -->|"書き込み反映"| DDB
4-1. Item Cache vs Query Cache の使い分け
DAX は 2 種類のキャッシュストアを内部に持つ。それぞれ異なる API 操作に対応している。
Item Cache
| 項目 | 内容 |
|---|---|
| 対象 API | GetItem, BatchGetItem |
| TTL デフォルト | 5 分 (300,000 ms) |
| キャッシュキー | テーブル名 + プライマリキー (Partition Key + Sort Key) |
| Hit 率計算 | ItemCacheHits / (ItemCacheHits + ItemCacheMisses) |
| 最適ユースケース | 単一アイテムの頻繁な読み取り |
Query Cache
| 項目 | 内容 |
|---|---|
| 対象 API | Query, Scan |
| TTL デフォルト | 1 分 (60,000 ms) |
| キャッシュキー | テーブル名 + クエリパラメータ全体 (完全一致) |
| Hit 率計算 | QueryCacheHits / (QueryCacheHits + QueryCacheMisses) |
| 最適ユースケース | 同一クエリ条件の繰り返し実行 |
ユースケース別 DAX 適用判断
テーブルの読み取りパターン分析
├── GetItem の RCU が全体の 80% 以上? → Item Cache 効果大 → DAX 推奨
├── 同一 Query パラメータが繰り返される? → Query Cache 効果大 → DAX 推奨
├── 読み取りに対して書き込みが多い (書き込み率 > 50%)? → Cache invalidation 頻発 → DAX 非推奨
└── Strongly Consistent Read が必須? → DAX 経由不可 → DAX 非推奨
CloudWatch での Hit 率監視:
import boto3
cloudwatch = boto3.client('cloudwatch', region_name='ap-northeast-1')
response = cloudwatch.get_metric_statistics(
Namespace='AWS/DAX',
MetricName='ItemCacheHits',
Dimensions=[{'Name': 'ClusterName', 'Value': 'my-dax-cluster'}],
StartTime='2026-05-17T00:00:00Z',
EndTime='2026-05-17T23:59:00Z',
Period=3600,
Statistics=['Sum'],
)
print(response['Datapoints'])
4-2. Read-Through — DAXがDynamoDBへフォールバック
Read-Through は DAX の中核機能だ。アプリケーションは DAX クライアントに対して通常の DynamoDB API を発行するだけでよく、キャッシュヒット/ミスの制御は DAX が透過的に行う。
Cache Miss 時の自動フォールバック
1. アプリ → DAX: GetItem(TableName, Key)
2. DAX: Item Cache 確認 → MISS
3. DAX → DynamoDB: GetItem(TableName, Key) (自動フォールバック)
4. DynamoDB → DAX: Item データ返却
5. DAX: Item Cache に格納 (TTL = 5分)
6. DAX → アプリ: Item データ返却
TTL 経過後の最初のアクセスは DynamoDB への読み取りが発生する。高頻度アクセスのテーブルでは TTL を短く設定しすぎると DynamoDB RCU が増加するため注意が必要だ。
Eventually Consistent Read 前提
DAX は Eventually Consistent Read のみサポートする。DynamoDB では以下の API で ConsistentRead=True が指定可能だが、DAX 経由の場合はキャッシュが返されるため Strongly Consistent にならない。
| API | ConsistentRead=True 指定時 | DAX 経由の動作 |
|---|---|---|
GetItem | Strongly Consistent | DAX キャッシュから返却 (Eventual のまま) |
Query | Strongly Consistent | DAX キャッシュから返却 (Eventual のまま) |
Strongly Consistent Read が必要な場合は DAX クライアントを使わず DynamoDB クライアントを直接使用する。
Python: DAX client vs DynamoDB client 使い分けコード例
import amazondax
import boto3
from typing import Optional
class DynamoDBRepository:
def __init__(self, table_name: str, dax_endpoint: str, region: str = 'ap-northeast-1'):
self.table_name = table_name
self.dax = amazondax.AmazonDaxClient(
endpoints=[dax_endpoint],
region_name=region,
)
self.dynamodb = boto3.client('dynamodb', region_name=region)
def get_item(self, key: dict, consistent_read: bool = False) -> Optional[dict]:
if consistent_read:
resp = self.dynamodb.get_item(
TableName=self.table_name,
Key=key,
ConsistentRead=True,
)
else:
resp = self.dax.get_item(
TableName=self.table_name,
Key=key,
)
return resp.get('Item')
def transact_write(self, transact_items: list) -> dict:
return self.dynamodb.transact_write_items(
TransactItems=transact_items,
)
4-3. Write-Through vs Write-Around
DAX の書き込みモードは 2 種類ある。テーブルの読み書きパターンに応じて選択する。
Write-Through (DAX 経由で書き込み)
アプリ → DAX: PutItem / UpdateItem
DAX → DynamoDB: 書き込み伝播
DAX: Item Cache を最新値で更新 → キャッシュ整合性を維持
特徴:
– キャッシュが最新データを保持 → 次の読み取りで Cache Hit
– DAX クライアントから全ての書き込みを行う場合に有効
– 読み取り多・書き込み少のテーブルに最適
Write-Around (DynamoDB 直接書き込み)
アプリ → DynamoDB: PutItem / UpdateItem (DAX をバイパス)
DAX: Cache は古いデータのまま (TTL 切れまで残存)
次の読み取り: Cache Miss → DynamoDB から最新データ取得 → Cache 更新
特徴:
– 書き込みは DynamoDB のスループットをそのまま活用
– DAX キャッシュは TTL 後に自動更新
– 書き込み多・読み取り少のテーブルや一括バッチ処理に適用
選定ガイド
| パターン | 推奨方式 | 理由 |
|---|---|---|
| 読み取り多 + 書き込み少 | Write-Through | キャッシュ整合性を維持しつつ読み取りを高速化 |
| 書き込み多 + 読み取り少 | Write-Around または DAX 不要 | キャッシュ効果が薄いため DAX のコストが割高 |
| バッチ一括投入後に高頻度読み取り | Write-Around + TTL 後に読み取り開始 | バッチ中は DynamoDB 直接書き込みで高スループット |
| リアルタイム更新 + 即時読み取り | Write-Through | 書き込み直後のキャッシュ整合性が重要 |
4-4. クラスタサイズ計算 + コスト最適化
ノードタイプ一覧 (東京リージョン)
| ノードタイプ | メモリ | vCPU | 価格 (On-Demand) |
|---|---|---|---|
| dax.r5.large | 13.5 GB | 2 | $0.269/h |
| dax.r5.xlarge | 27.0 GB | 4 | $0.538/h |
| dax.r5.2xlarge | 54.0 GB | 8 | $1.076/h |
| dax.r5.4xlarge | 108.0 GB | 16 | $2.152/h |
| dax.r5.8xlarge | 216.0 GB | 32 | $4.304/h |
| dax.r5.16xlarge | 432.0 GB | 64 | $8.608/h |
メモリ容量計算式
必要メモリ = キャッシュ対象データ量 × 1.3 (安全係数)
安全係数 1.3 の内訳: 内部管理オーバーヘッド10% + キャッシュキーメタデータ10% + 将来増加バッファ10%
例: キャッシュ対象データ量 30 GB → 必要メモリ = 30 × 1.3 = 39 GB → dax.r5.4xlarge (108 GB) が余裕を持った選択
コスト試算 (dax.r5.large, 3ノード構成)
| 料金区分 | 計算 | 月額コスト |
|---|---|---|
| On-Demand (3ノード) | $0.269 × 3 × 720h | 約 $581/月 |
| Reserved (1年, 3ノード) | On-Demand × 64% | 約 $372/月 |
| Reserved (3年, 3ノード) | On-Demand × 45% | 約 $261/月 |
Reserved Instance 選定: 本番環境で 6 ヶ月以上継続使用なら 1年 Reserved (36% 削減)、24 ヶ月以上の長期運用なら 3年 Reserved (55% 削減)。
DynamoDB RCU 削減効果との比較
Cache Hit 率 90% 時の RCU 削減効果:
– 元の RCU 消費: 10,000 RCU/秒
– DAX 導入後: 1,000 RCU/秒 (90% 削減)
– RCU 削減による DynamoDB コスト削減: 月約 $200〜$500 (規模による)
DAX クラスタコスト ($372/月) vs RCU 削減効果 ($200〜$500/月) を比較して導入判断を行う。
- ROI計算式: DAXコスト (dax.r5.large×3: 約$194/月) < DynamoDB RCU削減額 が導入条件
- 適用推奨: GetItem/BatchGetItem読み取りが月間1億回超のテーブル / レイテンシ要件がマイクロ秒単位
- 適用非推奨: Strongly Consistent Read必須の金融取引 / 書き込み多・読み取り少のテーブル / バッチ処理専用テーブル
- 段階的導入: まずTop-3高頻度テーブルのみDAX適用 → 効果計測後に拡大判断
4-5. DAX非対応操作 — TransactGetItems等の回避策
DAX がサポートしない操作は DynamoDB クライアントを直接使用する必要がある。
DAX 非対応操作一覧
| 非対応操作 | 代替策 |
|---|---|
TransactGetItems | DynamoDB client 直接使用 |
TransactWriteItems | DynamoDB client 直接使用 |
| PartiQL (ExecuteStatement 等) | DynamoDB client 直接使用 |
| DynamoDB Streams | DynamoDB client 直接使用 |
Parallel Scan (Segment 指定) | 一部制限あり、DynamoDB client 推奨 |
| コントロールプレーン API | DynamoDB client 使用 |
実装パターン: DAX + DynamoDB client 両方保持
import amazondax
import boto3
from typing import Optional, List
class DynamoDBDAXRepository:
"""DAX クライアントと DynamoDB クライアントを使い分けるリポジトリ"""
def __init__(
self,
table_name: str,
dax_endpoint: str,
region: str = 'ap-northeast-1',
):
self.table_name = table_name
self.dax = amazondax.AmazonDaxClient(
endpoints=[dax_endpoint],
region_name=region,
)
self.dynamodb = boto3.client('dynamodb', region_name=region)
def get_item(self, key: dict, consistent_read: bool = False) -> Optional[dict]:
"""通常読み取り: DAX 経由 (Eventually Consistent)"""
if consistent_read:
resp = self.dynamodb.get_item(
TableName=self.table_name,
Key=key,
ConsistentRead=True,
)
else:
resp = self.dax.get_item(
TableName=self.table_name,
Key=key,
)
return resp.get('Item')
def transact_write(self, transact_items: List[dict]) -> dict:
"""トランザクション書き込み: DAX 非対応 → DynamoDB 直接"""
return self.dynamodb.transact_write_items(
TransactItems=transact_items,
)
def batch_get_items(self, keys: List[dict]) -> List[dict]:
"""バッチ読み取り: DAX の BatchGetItem を活用"""
resp = self.dax.batch_get_item(
RequestItems={
self.table_name: {'Keys': keys}
}
)
return resp['Responses'].get(self.table_name, [])
Terraform: aws_dax_cluster + parameter_group + subnet_group 完全実装例
resource "aws_iam_role" "dax" {
name = "dax-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "dax.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy" "dax" {
name = "dax-dynamodb-access"
role = aws_iam_role.dax.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = [
"dynamodb:GetItem",
"dynamodb:BatchGetItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
]
Resource = "arn:aws:dynamodb:${var.aws_region}:${data.aws_caller_identity.current.account_id}:table/${var.dynamodb_table_name}"
}]
})
}
resource "aws_dax_subnet_group" "main" {
name = "dax-subnet-group"
subnet_ids = var.private_subnet_ids
}
resource "aws_dax_parameter_group" "main" {
name = "dax-prod-params"
parameters {
name = "query-ttl-millis"
value = "60000"
}
parameters {
name = "record-ttl-millis"
value = "300000"
}
}
resource "aws_security_group" "dax" {
name = "dax-sg"
description = "Security group for DAX cluster"
vpc_id= var.vpc_id
ingress {
description = "DAX from app tier (unencrypted)"
from_port = 8111
to_port= 8111
protocol = "tcp"
security_groups = [aws_security_group.app.id]
}
ingress {
description = "DAX from app tier (TLS)"
from_port = 9111
to_port= 9111
protocol = "tcp"
security_groups = [aws_security_group.app.id]
}
egress {
from_port= 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "dax-sg"
Environment = var.environment
}
}
resource "aws_dax_cluster" "main" {
cluster_name = "dax-prod-cluster"
iam_role_arn = aws_iam_role.dax.arn
node_type = "dax.r5.large"
replication_factor = 3
parameter_group_name = aws_dax_parameter_group.main.name
subnet_group_name = aws_dax_subnet_group.main.name
security_group_ids= [aws_security_group.dax.id]
server_side_encryption {
enabled = true
}
cluster_endpoint_encryption_type = "TLS"
maintenance_window = "sun:05:00-sun:06:00"
notification_topic_arn = var.sns_topic_arn
availability_zones = [
"${var.aws_region}a",
"${var.aws_region}c",
"${var.aws_region}d",
]
tags = {
Name = "dax-prod-cluster"
Environment = var.environment
}
}
resource "aws_cloudwatch_metric_alarm" "dax_item_cache_hit_rate" {
alarm_name = "dax-low-item-cache-hit-rate"
comparison_operator = "LessThanThreshold"
evaluation_periods = 3
threshold = 80
metric_query {
id = "hit_rate"
expression = "hits / (hits + misses) * 100"
label = "Item Cache Hit Rate (%)"
return_data = true
}
metric_query {
id = "hits"
metric {
metric_name = "ItemCacheHits"
namespace= "AWS/DAX"
period= 300
stat = "Sum"
dimensions = {
ClusterName = aws_dax_cluster.main.cluster_name
}
}
}
metric_query {
id = "misses"
metric {
metric_name = "ItemCacheMisses"
namespace= "AWS/DAX"
period= 300
stat = "Sum"
dimensions = {
ClusterName = aws_dax_cluster.main.cluster_name
}
}
}
alarm_description = "DAX Item Cache Hit Rate dropped below 80%"
alarm_actions = [var.sns_topic_arn]
}
- 全テーブルに DAX を適用する → 読み取り頻度が低いテーブルへの DAX 適用はコスト増のみで効果なし。CloudWatch で RCU 消費量を分析し、読み取り頻度の高いテーブルに限定して適用すること
- Strongly Consistent Read を DAX 経由で発行する → DAX はキャッシュを返すため Strongly Consistent にならない。金融取引・在庫確認など整合性が重要な操作は DynamoDB client を直接使用すること
- Eventually Consistent を前提としない設計で DAX を導入する → TTL 期間中は古いデータが返される場合がある。設計段階で Eventually Consistent を前提とし、整合性が必要な箇所を明確に分離すること
5. MemoryDB for Redis 本番運用 — Durable Redis × Multi-Region × Snapshot
graph TB
subgraph Region["ap-northeast-1"]
APP["Application\n(ECS/Lambda)"]
subgraph Cluster["MemoryDB Cluster"]
subgraph AZ1["AZ: ap-northeast-1a"]
Primary["Primary Node\n(Read/Write)"]
end
subgraph AZ2["AZ: ap-northeast-1c"]
Replica1["Replica Node\n(Read Only)"]
end
subgraph AZ3["AZ: ap-northeast-1d"]
Replica2["Replica Node\n(Read Only)"]
end
WAL["Multi-AZ Transaction Log\n(Write-Ahead Log)\nRPO = 0"]
end
S3["S3 Bucket\n(Snapshot Export)"]
DR["DR Region\nap-southeast-1\n(Cross-Region Restore)"]
end
APP -->|"6379 (TLS)"| Primary
APP -->|"6379 (TLS)"| Replica1
APP -->|"6379 (TLS)"| Replica2
Primary -->|"Sync Write"| WAL
WAL -->|"Sync Replication"| Replica1
WAL -->|"Sync Replication"| Replica2
Primary -->|"Daily Snapshot"| S3
S3 -.->|"Cross-Region Export"| DR
MemoryDB for Redis は ElastiCache Redis と同じ Redis 互換 API を持ちながら、Multi-AZ Transaction Log (WAL) によるデータ永続化を提供する。金融取引・EC 注文・在庫管理など「書き込みデータをゼロロスで保護しなければならない」ユースケースを主なターゲットとする。
5-1. Durable Redis — AOFログ永続化のしくみ
MemoryDB の根幹を成す機能が Multi-AZ Transaction Log である。すべての書き込みは Redis のメモリに反映される前に、複数の AZ に分散配置されたトランザクションログへ同期的に書き込まれる。この仕組みにより RPO = 0(データ損失ゼロ) を保証する。
ElastiCache Redis との永続化比較
| 項目 | ElastiCache Redis | MemoryDB for Redis |
|---|---|---|
| 永続化方式 | RDB スナップショット + AOF (非同期) | Multi-AZ Transaction Log (同期 WAL) |
| RPO | > 0(最大数秒〜数分のデータロスあり) | = 0(ゼロロス保証) |
| レプリケーション | 非同期 | 同期 |
| フェイルオーバー後のデータ | Replica が Primary に昇格 (一部ロスあり) | WAL から完全リカバリ |
| コスト | 標準 | ElastiCache の約 2 倍 |
ElastiCache Redis は非同期レプリケーションを採用しているため、Primary がクラッシュした場合に Replica との差分データが失われる可能性がある。MemoryDB は書き込みのたびに WAL へ同期コミットするため、フェイルオーバー後も WAL からデータを完全にリカバリできる。
MemoryDB を選ぶべきシナリオ
- 金融取引: 決済・送金トランザクションのステータスをセッション DB として保持する場合
- EC 注文: カート情報・在庫引き当て・注文確定フローで中間ステータスを保持する場合
- ゲームランキング: Sorted Set でリアルタイムランキングを管理し、障害時も順位データを消失させたくない場合
- IoT テレメトリ: センサーデータを時系列でバッファリングし、ダウンストリームへの再送処理まで保持する場合
逆に、セッション管理や API レスポンスキャッシュなど「失われても再生成できる」データには ElastiCache Redis が適切でコストも低い。
5-2. Multi-AZ / Multi-Region クラスタ設計
Multi-AZ 構成(同一リージョン)
MemoryDB クラスタは Primary Node と複数の Replica Node を別 AZ に分散配置する。1 AZ 障害が発生しても残りの AZ の Replica が継続してサービスを提供し、WAL から整合性を担保しながら新 Primary を選出する。
推奨構成:
– Primary × 1: 書き込みエンドポイント (cluster-endpoint)
– Replica × 2 (別 AZ): 読み取りエンドポイント (reader-endpoint)
– シャード数: 読み取りスケールが必要な場合は 2〜4 シャードに分割
Multi-Region 構成(DR 用途)
MemoryDB は現時点でネイティブのグローバルデータストア機能を持たないが、Snapshot → S3 エクスポート → クロスリージョン S3 コピー → DR リージョンでリストアというパターンで DR を実現できる。RPO はスナップショット間隔(最短 1 時間)となる点に注意する。
Subnet Group 設計
MemoryDB はプライベートサブネットにのみ配置する。パブリックサブネットへの配置は不可。
resource "aws_memorydb_subnet_group" "main" {
name = "memorydb-private-subnet-group"
subnet_ids = [
aws_subnet.private_1a.id,
aws_subnet.private_1c.id,
aws_subnet.private_1d.id,
]
tags = {
Name = "memorydb-private-subnet-group"
Env = "production"
}
}
セキュリティグループ設計
アプリケーション層(ECS タスクや Lambda 関数)の SG から MemoryDB SG の 6379 番ポートへのインバウンドのみを許可する。インターネットからの直接アクセスは遮断する。
resource "aws_security_group" "memorydb" {
name = "memorydb-sg"
description = "MemoryDB cluster security group"
vpc_id= aws_vpc.main.id
ingress {
from_port = 6379
to_port= 6379
protocol = "tcp"
security_groups = [aws_security_group.app.id]
description = "Allow Redis traffic from application layer"
}
egress {
from_port= 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "memorydb-sg"
Env = "production"
}
}
5-3. Snapshot → S3エクスポート / クロスリージョンリストア
自動スナップショット
MemoryDB は毎日自動スナップショットを取得する。snapshot_retention_limit で保持日数(最大 35 日)を指定する。メンテナンスウィンドウ外の深夜帯をスナップショット取得時刻に設定することで、本番トラフィックへの影響を最小化できる。
手動スナップショット(重要操作前の必須手順)
# 手動スナップショット作成
aws memorydb create-snapshot \
--cluster-name my-memorydb-cluster \
--snapshot-name pre-maintenance-$(date +%Y%m%d) \
--region ap-northeast-1
# スナップショット一覧確認
aws memorydb describe-snapshots \
--cluster-name my-memorydb-cluster \
--region ap-northeast-1 \
--query 'Snapshots[*].{Name:Name,Status:Status,CreatedAt:CreationTime}'
手動スナップショットはクラスター削除後も保持されるため、クラスターを削除する前に必ず取得する。
S3 エクスポート手順
# スナップショットを S3 にエクスポート
aws memorydb export-snapshot \
--snapshot-name pre-maintenance-20260517 \
--s3-bucket-name my-memorydb-backup-bucket \
--region ap-northeast-1
# エクスポートステータス確認
aws memorydb describe-snapshots \
--snapshot-name pre-maintenance-20260517 \
--region ap-northeast-1 \
--query 'Snapshots[0].Status'
S3 バケットには memorydb.amazonaws.com への s3:PutObject と s3:GetObject 権限を付与したバケットポリシーが必要となる。
クロスリージョンリストア(DR 手順)
- 東京リージョンのスナップショットを S3 にエクスポート
aws s3 cpで DR リージョンの S3 バケットにコピー- DR リージョンで
aws memorydb create-cluster --snapshot-arnsによりリストア
リストア時間目安:
– 10 GB: 約 10 分
– 50 GB: 約 30 分
– 100 GB: 約 60 分
実際のリストア時間はネットワーク帯域やシャード数によって変動するため、DR 訓練時に計測しておくことを推奨する。
- RPO目標: AOFログ永続化により障害直前まで復旧可 (ElastiCache Redisよりデータ損失リスクが低い)
- Snapshot頻度: 本番は日次自動Snapshot + 変更前の手動Snapshot必須
- DR用途: クロスリージョンSnapshotでRTOを短縮 — us-east-1のSnapshotをap-northeast-1にコピーして事前準備
- コスト注意: Snapshotストレージは $0.023/GB-month (東京) — 大容量クラスタでは月次コストを試算すること
5-4. ElastiCache Redis → MemoryDB マイグレーション
マイグレーション手順(ダウンタイム最小化)
1. ElastiCache Redis で RDB スナップショット取得
↓
2. スナップショットを S3 にエクスポート
↓
3. S3 スナップショットから MemoryDB クラスターを作成
↓
4. データ整合性確認(キー数・TTL・データ型)
↓
5. アプリケーション設定変更(エンドポイント切替)
↓
6. 本番トラフィックを MemoryDB にルーティング(Blue-Green)
↓
7. 旧 ElastiCache Redis を削除
Blue-Green 切替のポイント
- 切替前に ElastiCache と MemoryDB を並行稼働させ、書き込みを二重化するか、メンテナンスウィンドウ中に切替を実施する
- アプリケーションの Redis クライアントライブラリが Redis 7.x 互換であることを確認する(MemoryDB は Redis 7.x 互換)
- TLS 接続が必須となるため、ElastiCache で TLS を有効化していない場合はクライアント設定の変更が必要
Terraform での移行準備
# ElastiCache Redis から S3 スナップショットをインポートして MemoryDB を作成
resource "aws_memorydb_cluster" "migrated" {
name = "memorydb-migrated"
node_type = "db.r6g.large"
num_shards = 1
num_replicas_per_shard = 2
subnet_group_name= aws_memorydb_subnet_group.main.name
security_group_ids = [aws_security_group.memorydb.id]
acl_name= aws_memorydb_acl.main.name
tls_enabled= true
parameter_group_name= aws_memorydb_parameter_group.main.name
# ElastiCache スナップショットから初期データをインポート
snapshot_arns = ["arn:aws:s3:::my-elasticache-backup-bucket/snapshot.rdb"]
tags = {
Name = "memorydb-migrated"
MigratedAt = "2026-05-17"
}
}
5-5. MemoryDB vs ElastiCache Redis 選定基準
選定マトリクス
| 要件 | MemoryDB for Redis | ElastiCache Redis |
|---|---|---|
| データ永続保証(RPO = 0) | ✅ WAL 同期書き込み | ❌ 一部ロスあり(非同期) |
| Multi-AZ フェイルオーバー | ✅ | ✅ |
| 読み取りスケールアウト | ✅ Replica 追加 | ✅ Replica 追加 |
| Redis 互換バージョン | Redis 7.x | Redis 7.x |
| TLS 接続 | 必須 | オプション |
| コスト | 高(ElastiCache の約 2 倍) | 標準 |
| ユースケース | 金融取引 / EC 注文 / 在庫 | セッション / API キャッシュ |
判断フロー
書き込みデータの損失が許容できない?
├─ YES → MemoryDB for Redis
│ (金融・EC・在庫・ゲームランキング)
└─ NO → データは揮発してよい(再生成可能)?
├─ YES → ElastiCache Redis
│ (セッション・APIキャッシュ・一時データ)
└─ 要検討 → データサイズ・コスト・SLA を精査
- 基本料金が約 2 倍: MemoryDB の Durability コストは ElastiCache Redis と比較して約 2 倍。月次コストは事前に AWS Pricing Calculator で試算すること。
- Multi-Region 追加課金: クロスリージョンのスナップショットコピーには S3 転送料金と S3 ストレージ料金が発生する。DR 要件の RTO/RPO に見合うかコスト評価が必要。
- Snapshot 保管コスト:
snapshot_retention_limit=7以上に設定すると S3 ストレージが線形に増加する。不要なスナップショットは定期的に手動削除またはライフサイクルポリシーで自動削除する。 - TLS オーバーヘッド: TLS 必須のため、大量の小さいコマンドを扱うワークロードでは CPU/レイテンシへの影響を事前に負荷テストで計測すること。
完全 Terraform 実装例
# MemoryDB ACL(アクセス制御リスト)
resource "aws_memorydb_acl" "main" {
name = "memorydb-prod-acl"
user_names = [aws_memorydb_user.app.name]
tags = {
Name = "memorydb-prod-acl"
Env = "production"
}
}
resource "aws_memorydb_user" "app" {
user_name = "app-user"
access_string = "on ~* &* +@all"
authentication_mode {
type= "password"
passwords = [var.memorydb_password]
}
tags = {
Name = "memorydb-app-user"
Env = "production"
}
}
# MemoryDB Parameter Group
resource "aws_memorydb_parameter_group" "main" {
name= "memorydb-prod-params"
family = "memorydb_redis7"
parameter {
name = "activedefrag"
value = "yes"
}
parameter {
name = "lazyfree-lazy-eviction"
value = "yes"
}
parameter {
name = "maxmemory-policy"
value = "allkeys-lru"
}
tags = {
Name = "memorydb-prod-params"
Env = "production"
}
}
# MemoryDB Cluster(本番構成)
resource "aws_memorydb_cluster" "main" {
name = "memorydb-prod"
node_type = "db.r6g.large"
num_shards = 2
num_replicas_per_shard = 2
# Durability 設定
tls_enabled = true
# ネットワーク
subnet_group_name = aws_memorydb_subnet_group.main.name
security_group_ids = [aws_security_group.memorydb.id]
# ACL / Parameter Group
acl_name = aws_memorydb_acl.main.name
parameter_group_name = aws_memorydb_parameter_group.main.name
# Snapshot 設定
snapshot_retention_limit = 7
snapshot_window = "02:00-04:00"
# メンテナンスウィンドウ(JST 深夜 3:00-5:00 = UTC 18:00-20:00)
maintenance_window = "sun:18:00-sun:20:00"
tags = {
Name = "memorydb-prod"
Env = "production"
}
}
# 接続エンドポイント出力
output "memorydb_cluster_endpoint" {
description = "MemoryDB cluster endpoint (write)"
value = aws_memorydb_cluster.main.cluster_endpoint[0].address
}
output "memorydb_reader_endpoint" {
description = "MemoryDB reader endpoint"
value = aws_memorydb_cluster.main.cluster_endpoint[0].address
}
- 金融サービス: 決済ステータス・残高キャッシュ・不正検知スコア。ゼロロス要件が厳格なため MemoryDB が最適。
- EC サイトの注文・在庫: 在庫引き当てロック・カート確定ステータス。オーバーセルを防ぐには RPO = 0 が必須。
- ゲームのリアルタイムランキング: Sorted Set によるスコアボード。障害後もランキングを完全復元できる MemoryDB が信頼性を高める。
- 広告配信のフリークエンシーキャップ: ユーザーへの広告表示回数制限をカウンターで管理。消えると過配信につながるためデータ保護が重要。
- セッション管理(高可用性要件): 通常は ElastiCache で十分だが、ログアウト強制が法規制要件となる金融・医療系はMemoryDB を採用するケースもある。
6. キャッシュ本番運用 詰まりポイント7選
本番環境でキャッシュ層を運用していると、設計段階では見えなかった問題が次々と顕在化する。ElastiCache・DAX・MemoryDBを問わず共通して発生しやすい7つの詰まりポイントと、その根本対策を解説する。
6-1. Cache Stampede / Dog-piling 対策
問題
人気コンテンツのTTLが同時に切れると、数百〜数千のリクエストが一斉にOriginDB(RDS・DynamoDB)へ流れ込む。キャッシュが空のため全リクエストがDB直撃となり、DBが過負荷でダウンする連鎖障害(Cache Stampede / Dog-piling)が発生する。
原因
- 同一キーに同一TTLを設定 → 一斉expire
- 高トラフィック時にexpireと同時に大量リクエストが集中
- ロック機構がないためDB読み取りが重複実行される
対策1: Probabilistic Early Expiration(確率的早期更新)
TTLの残り10%に入った時点でランダム確率でキャッシュを早期更新する手法。完全なロックを使わずStampedeを分散させられる。
import random
import time
import redis
r = redis.Redis(host='my-cluster.cache.amazonaws.com', port=6379, ssl=True)
def get_with_probabilistic_refresh(key: str, fetch_fn, ttl: int = 300):
"""
Probabilistic Early Expiration でCache Stampedeを防止する。
TTL残り10%以下になったとき、確率的に早期再フェッチを実行する。
"""
value = r.get(key)
remaining_ttl = r.ttl(key)
# TTL残り10%以下かつ30%の確率で早期更新
if value is not None and remaining_ttl < ttl * 0.1:
if random.random() < 0.3:
new_value = fetch_fn()
r.setex(key, ttl, new_value)
return new_value
if value is None:
value = fetch_fn()
r.setex(key, ttl, value)
return value
対策2: Mutex Lock(分散ロック)
RedisのSET NX EXを使い、最初のリクエストのみがDB読み取りを行うフラグを立てる。他のリクエストは待機してキャッシュの更新を待つ。
-- Lua スクリプト: 原子的ロック取得
-- KEYS[1]: cache key, KEYS[2]: lock key
-- ARGV[1]: TTL (秒), ARGV[2]: lock TTL (秒)
local value = redis.call('GET', KEYS[1])
if value then
return value
end
local lock = redis.call('SET', KEYS[2], '1', 'NX', 'EX', ARGV[2])
if lock then
-- このプロセスがフェッチを担当
return nil -- caller側でDB読み取りを実行してキャッシュに書き戻す
else
-- 他プロセスがフェッチ中: 短時間待機して再取得
redis.call('WAIT', 0, 100) -- 100ms待機
return redis.call('GET', KEYS[1])
end
対策3: Refresh-Ahead(事前更新)
バックグラウンドジョブがTTL期限前にキャッシュを更新する方式。ユーザーリクエストのレイテンシに影響せずキャッシュをホットに保てる。EventBridge Schedulerや Lambda関数でキーリストを定期的に再フェッチする設計が実践的。

6-2. Cold Start問題 — Warm-up戦略
問題
ElastiCache再起動・スケールアウト直後・新ノード追加直後はキャッシュが空(Cold Start)となる。この状態でトラフィックが流れ込むと、すべてのリクエストがOriginDBに直撃し、DBが応答不能になるケースがある。
原因
- デプロイスクリプトにCache事前投入ステップが含まれていない
- ElastiCache Cluster Modeでシャードが再割り当てされ既存キャッシュが無効化
- Auto Scalingで新ノードが追加された際のキャッシュ引き継ぎ未設計
対策1: デプロイ前のCache Pre-population
高頻度アクセスキーのリストをS3に保持し、デプロイスクリプトの最後にWarm-upジョブを実行する。
#!/bin/bash
# pre_populate_cache.sh: デプロイ後のキャッシュ事前投入
REDIS_HOST="my-cluster.cache.amazonaws.com"
S3_KEY_LIST="s3://my-app-config/cache-warmup-keys.json"
LOCAL_KEY_LIST="/tmp/warmup_keys.json"
# S3からキーリストを取得
aws s3 cp "$S3_KEY_LIST" "$LOCAL_KEY_LIST"
echo "[Warm-up] キャッシュ事前投入を開始します"
python3 - <<'PYEOF'
import json
import redis
import boto3
import requests
r = redis.Redis(host='my-cluster.cache.amazonaws.com', port=6379, ssl=True)
with open('/tmp/warmup_keys.json') as f:
warmup_keys = json.load(f)
for item in warmup_keys:
key = item['key']
api_url = item['source_url']
ttl = item.get('ttl', 300)
# APIから最新データを取得してキャッシュに格納
resp = requests.get(api_url, timeout=5)
if resp.status_code == 200:
r.setex(key, ttl, resp.text)
print(f" [OK] {key} (TTL={ttl}s)")
else:
print(f" [SKIP] {key} - status={resp.status_code}")
print("[Warm-up] 完了")
PYEOF
対策2: Background Warm-up Job(Lambda)
Lambda関数をEventBridge Schedulerで5分おきに実行し、人気コンテンツをバックグラウンドでキャッシュ更新し続ける。Cloud Start時の初回ロードでも自動的にWarm-upが走るため人手介入が不要になる。
対策3: CloudFront + Cache-Control でエッジWarm-up
ElastiCacheより上位のエッジキャッシュ(CloudFront)を先行Warm-upすることで、ElastiCacheへのリクエスト集中そのものを防ぐ。Cache-Control: max-age=3600 を適切に設定し、CloudFrontがOriginキャッシュとして機能する設計が有効。
6-3. キャッシュ不整合 — Write-Behind Lag
問題
DBへの書き込みとキャッシュへの反映にタイムラグが生じ、古いデータをユーザーに返してしまう。特にWrite-Behind(非同期書き込み)パターンでは、DBが更新されるまでの間にキャッシュとDBが不整合状態となる。
キャッシュパターン比較
| パターン | 読み取り | 書き込み | 整合性 | 推奨用途 |
|---|---|---|---|---|
| Cache-Aside | キャッシュmiss→DB→キャッシュ更新 | DBのみ更新 (キャッシュ手動無効化) | 結果整合 | 読み取り重視・汎用 |
| Read-Through | キャッシュ層が自動でDB読取 | アプリ→DB→キャッシュ自動更新 | 結果整合 | ORM統合・DAX |
| Write-Through | アプリ→キャッシュ+DBを同期書込 | 両方同期更新 | 強整合 | 書き込み頻度低・金融 |
| Write-Behind | アプリ→キャッシュのみ即時・DB非同期 | キャッシュ→DBは非同期 | 結果整合 (遅延あり) | 書き込み頻度高・ログ |
Write-Behind Lag 対策
TTL短縮: Write-Behindパターンで整合性が求められるデータはTTLを30〜60秒に短縮し、キャッシュの自然期限切れを整合ポイントとして活用する。
Event-Driven Cache Invalidation: DynamoDB Streamsや RDS Aurora with PostgreSQLのLogical Replicationを使い、DB変更イベントをトリガーにキャッシュを即時invalidateする。
import boto3
import redis
import json
r = redis.Redis(host='my-cluster.cache.amazonaws.com', port=6379, ssl=True)
def lambda_handler(event, context):
"""
DynamoDB Streams トリガー: DB更新を検知してキャッシュを無効化する。
"""
for record in event.get('Records', []):
if record['eventName'] in ('MODIFY', 'REMOVE'):
keys = record['dynamodb'].get('Keys', {})
item_id = keys.get('PK', {}).get('S', '')
# 関連キャッシュを全て無効化
cache_key = f"product:{item_id}"
deleted = r.delete(cache_key)
print(f"Cache invalidated: {cache_key} (deleted={deleted})")
6-4. TTL設計のアンチパターン
アンチパターン1: 全キーTTL=86400(固定1日)
すべてのキャッシュキーにTTL=86400を設定すると、データの変化頻度に関わらず1日後まで古いデータが返り続ける。商品価格・在庫情報・ユーザープロフィールなど頻繁に変わるデータが陳腐化する。
アンチパターン2: TTL=60(短すぎ)
全キーに1分TTLを設定すると、キャッシュヒット率が下がりOriginDBへのリクエストが増大する。DBのコネクション数・CPU使用率が跳ね上がり、スパイク時に過負荷状態になる。
正解: 用途別TTL階層設計
データの変化頻度と整合性要求に基づきTTLを分類する。
| データ種別 | TTL | 理由 |
|---|---|---|
| 静的アセット(画像/CSS/JS) | 3600s(1時間) | 変更頻度が低く長期キャッシュが有効 |
| 商品情報・カタログ | 300s(5分) | 定期更新されるが即時反映不要 |
| セッション・認証トークン | 1800s(30分) | ユーザー操作タイムアウトに合わせる |
| APIレスポンス(動的) | 60s(1分) | 頻繁に変化するが短期間は許容 |
| 在庫・価格(高整合性) | 30s(30秒) | 誤表示リスクを最小化 |
TTL Jitter(揺らぎ)でStampede防止
同一種別のキーに同一TTLを設定すると一斉expireが起きる。TTLに±10%のランダム揺らぎを加えることで自然に分散できる。
import random
def get_jittered_ttl(base_ttl: int, jitter_pct: float = 0.1) -> int:
"""
TTLにjitterを加えてCache Stampedeを防止する。
例: base_ttl=300 → 270〜330秒のランダム値を返す
"""
jitter = int(base_ttl * jitter_pct)
return base_ttl + random.randint(-jitter, jitter)
# 使用例
r.setex('product:123', get_jittered_ttl(300), product_data)
CloudWatch監視: TTL別CacheMisses
ElastiCacheのCloudWatchメトリクスCacheMissesをTTL区分のタグ別に監視し、特定TTL区分のミス率が急増していないか定期確認する。
- TTL階層設計: 静的コンテンツ3600秒 / 動的データ300秒 / セッション1800秒 — 用途別に使い分ける
- TTL Jitter:
TTL = base_ttl + random(0, base_ttl * 0.1)でCache Stampede防止 - TTL=0 (無期限) 禁止: Evictionポリシーに依存した不確定な挙動を招く — 必ず明示的TTLを設定
- 固定TTLの罠: 全キーに同一TTL → 期限切れが集中してOrigin DB過負荷。Jitterで分散させること
6-5. Memory Fragmentation
問題
BytesUsedForCacheがmaxmemoryの70%程度なのにEvictionが発生したり、メモリ使用率が想定より早く上昇するケースがある。メモリフラグメンテーションが原因であることが多い。
原因
Redisはjemmallocを使ってメモリ管理しているが、異なるサイズのキーを頻繁に作成・削除すると断片化が進む。mem_fragmentation_ratioが1.5を超えると、論理的には空きがあっても物理メモリが断片化して有効活用できない状態になる。
診断: INFO memory コマンド
# Redis CLI で mem_fragmentation_ratio を確認
redis-cli -h my-cluster.cache.amazonaws.com -p 6379 --tls INFO memory | \
grep -E "used_memory_human|used_memory_rss_human|mem_fragmentation_ratio|active_defrag"
# 期待出力例:
# used_memory_human: 2.50G
# used_memory_rss_human: 4.10G
# mem_fragmentation_ratio: 1.64 ← 1.5超えは要対処
# active_defrag_running: 0
対策: Active Defragmentation
redis.conf(またはElastiCacheパラメータグループ)で以下を設定し、Redisが実行時に断片化を自動解消するようにする。
# ElastiCache パラメータグループ設定 (redis.conf 相当)
# active-defrag-enabled: yes
# active-defrag-threshold-lower: 10 (fragmentation ratio 10% 超で開始)
# active-defrag-threshold-upper: 100
# active-defrag-ignore-bytes: 104857600 (100MB 未満は無視)
# AWS CLI でパラメータグループを更新する例
aws elasticache modify-cache-parameter-group \
--cache-parameter-group-name my-redis-params \
--parameter-name-values \
ParameterName=activedefrag,ParameterValue=yes \
ParameterName=active-defrag-threshold-lower,ParameterValue=10
CloudWatch監視項目
| メトリクス | 警告しきい値 | 説明 |
|---|---|---|
BytesUsedForCache | maxmemoryの80%超 | メモリ逼迫の早期検知 |
CurrItems | 急増パターン | キー爆発の検知 |
SwapUsage | 0を超えた場合 | スワップ発生は深刻なメモリ不足 |
Evictions | 0を超えた場合 | データ損失が始まっているサイン |
6-6. 接続プール枯渇 — Connection Pooling
問題
アプリサーバー台数が増えると、各サーバーからElastiCacheへのTCPコネクション数が線形に増加する。ElastiCacheノードのCurrConnectionsがmaxclientsに近づくと新規接続が拒否され、タイムアウトエラーが連発する。
ElastiCacheのデフォルト上限
| インスタンスタイプ | maxclients デフォルト |
|---|---|
| cache.t3.micro | 65000 |
| cache.r7g.large | 65000 |
| cache.r7g.4xlarge | 65000 |
maxclientsの上限は高いが、コネクションを使い捨て(毎リクエスト新規接続)する実装では接続ハンドシェイクのオーバーヘッドが積み重なりレイテンシが悪化する。コネクションプールを使って接続を再利用することが重要。
対策: アプリ側コネクションプール設定
import redis
# Python redis-py: アプリ起動時に一度だけConnectionPoolを生成し、
# モジュールレベルで共有することでコネクションを再利用する
_pool = redis.ConnectionPool(
host='my-cluster.cache.amazonaws.com',
port=6379,
ssl=True,
max_connections=50, # 1プロセスあたり最大50コネクション
socket_timeout=1.0, # 読み取りタイムアウト 1秒
socket_connect_timeout=0.5,
retry_on_timeout=True,
)
def get_redis_client() -> redis.Redis:
"""アプリケーション全体で共有するRedisクライアントを返す。"""
return redis.Redis(connection_pool=_pool)
Java (Lettuce) の場合: StatefulRedisConnectionはスレッドセーフなため、シングルトンとして保持してスレッドプールと組み合わせる。
Go (go-redis) の場合: redis.NewClient時にPoolSizeを設定し、MinIdleConnsを指定してウォームプールを維持する。
CloudWatch監視: CurrConnections
CurrConnectionsがmaxclientsの70%(約45500)を超えたらアラームを発報し、コネクションリークや接続プール設定の見直しをトリガーする。
6-7. セキュリティグループ誤設定によるタイムアウト
問題
ElastiCache(Redis/Memcached)への接続がタイムアウトエラーになる、または断続的に接続が失敗する。アプリログにはConnection timed outやConnection refusedが記録される。
原因
VPC内のセキュリティグループ設定ミスが最多原因。ElastiCache側のSGにアプリ層からのinboundルールが未設定、またはアプリ層SGのoutboundが制限されているケースが典型。
確認手順
# 1. ElastiCacheクラスターに紐付くSG IDを確認
aws elasticache describe-cache-clusters \
--cache-cluster-id my-redis-cluster \
--query 'CacheClusters[].SecurityGroups[].SecurityGroupId' \
--output text
# 2. 対象SGのinboundルールを確認 (Redis: 6379, Memcached: 11211)
aws ec2 describe-security-groups \
--group-ids sg-xxxxxxxxxxxxxxxxx \
--query 'SecurityGroups[].IpPermissions[]'
# 3. VPC Flow Logs で接続拒否 (REJECT) を確認
aws logs filter-log-events \
--log-group-name /vpc/flowlogs \
--filter-pattern 'REJECT' \
--start-time $(date -d '1 hour ago' +%s000) | \
grep ':6379 '
Terraform: セキュリティグループ設定例
# ElastiCache 用セキュリティグループ
resource "aws_security_group" "elasticache" {
name = "elasticache-sg"
description = "ElastiCache Redis security group"
vpc_id= var.vpc_id
}
# アプリ層 SG からの inbound を許可 (Redis port 6379)
resource "aws_vpc_security_group_ingress_rule" "elasticache_from_app" {
security_group_id= aws_security_group.elasticache.id
referenced_security_group_id = aws_security_group.app.id
from_port = 6379
to_port = 6379
ip_protocol= "tcp"
description= "Allow Redis from app layer"
}
# アプリ層セキュリティグループ (参照用)
resource "aws_security_group" "app" {
name = "app-sg"
description = "Application layer security group"
vpc_id= var.vpc_id
}
# アプリ層 → ElastiCache への outbound を明示的に許可
resource "aws_vpc_security_group_egress_rule" "app_to_elasticache" {
security_group_id= aws_security_group.app.id
referenced_security_group_id = aws_security_group.elasticache.id
from_port = 6379
to_port = 6379
ip_protocol= "tcp"
description= "Allow outbound to ElastiCache Redis"
}
Memcachedの場合: portを6379→11211に変更するのみ。設計パターンは同一。
- ✅ Multi-AZ + Auto-Failover: ElastiCacheのMulti-AZ設定とAuto-Failoverが有効になっているか確認(AWS コンソール→ ElastiCache→クラスター→設定タブ)
- ✅ TTL階層設計レビュー: データ種別ごとのTTLが用途別TTL表に準拠しているか確認。固定TTL(86400 / 60)が残っていないかgrepで確認
- ✅ Cache Stampede対策: ProbabilisticEarlyExpiration・MutexLock・TTL Jitterのいずれかが実装されているか確認
- ✅ Warm-up戦略: デプロイスクリプトにCold Start対策(pre-population jobまたはCloudFrontWarm-up)が含まれているか確認
- ✅ CloudWatch Alarm設定: Evictions・CPUUtilization・SwapUsage・CurrConnectionsの各アラームがしきい値つきで設定されているか確認
- ✅ コネクションプール: アプリ側Redisクライアントがシングルトン+ConnectionPoolで実装されており、毎リクエスト新規接続していないことをコードレビューで確認
- ✅ セキュリティグループ: ElastiCache SGのinboundにアプリ層SGからのport 6379(Redis)または11211(Memcached)が明示的に許可されているか確認
- ✅ Active Defragmentation: パラメータグループで
activedefrag=yesが設定されているか確認。mem_fragmentation_ratioが1.5未満であることを事前測定
7. アンチパターン → 正解パターン変換演習 5問
ElastiCache・DAX・MemoryDB の本番運用でよく発生するアンチパターン5問を解説する。
各問でNG構成の問題点を分析し、正解パターンへの変換コードと設計指針を示す。
実際の事例に基づいた問題ばかりのため、本番設計の最終チェックリストとして活用されたい。
Q1. Single-AZ ElastiCache でフェイルオーバ失敗
アンチパターン
# NG: Single-AZ / Auto-Failover無効
resource "aws_elasticache_replication_group" "ng" {
automatic_failover_enabled = false
num_cache_clusters= 1
}
automatic_failover_enabled = false かつ num_cache_clusters = 1 の Primary-Only 構成では、
AZ障害またはノード障害発生時に自動昇格が発動しない。
ElastiCache に依存するアプリケーション全体が 手動復旧まで停止 し、
運用担当者がコンソールまたは CLI で手動フェイルオーバを実行するまでダウンタイムが継続する。
正解パターン
# OK: Multi-AZ / Auto-Failover有効
resource "aws_elasticache_replication_group" "ok" {
replication_group_id = "prod-cache"
description = "Production cache with Multi-AZ"
automatic_failover_enabled = true
multi_az_enabled = true
num_cache_clusters= 3
availability_zones= ["ap-northeast-1a", "ap-northeast-1c", "ap-northeast-1d"]
node_type= "cache.r7g.large"
at_rest_encryption_enabled = true
transit_encryption_enabled = true
}
解説: Failover動作と本番必須設定
| 項目 | 推奨値 | 理由 |
|---|---|---|
automatic_failover_enabled | true | Replica → Primary 自動昇格 (10〜30秒) |
multi_az_enabled | true | automatic_failover_enabled と同時設定が必須 |
num_cache_clusters | 3以上 | Primary + Replica×2、2 AZ以上に分散 |
| DNS TTL (ElastiCache) | 60秒 | SDK 側で再接続ロジックと組み合わせて使用 |
Failover 後の DNS 伝播遅延 (最大60秒) を考慮し、アプリ側で接続リトライを実装すること。redis-py の retry_on_error / retry_on_timeout オプションを活用することで、
DNS 更新を待たずに自動再接続が可能になる。
Q2. TTL未設定でデータ陳腐化 + Origin過負荷
アンチパターン
# NG: TTL未指定 → メモリ枯渇 + 陳腐化データ返却
r.set("user:profile:1001", user_json)
# NG: 全データ固定1日 → 更新頻度無視で Cache Stampede 発生
r.setex("product:price:2001", 86400, price_json)
TTL未設定のキーはメモリが枯渇するまで Eviction されないため、陳腐化データが返却され続ける。
固定TTLでは、一定時刻に大量のキーが一斉期限切れになると Cache Stampede が発生し、
OriginDB (RDS/DynamoDB) へ膨大なリクエストが集中してカスケード障害につながる。
正解パターン: 用途別 TTL 階層設計 + Jitter
import redis
import random
import json
r = redis.Redis(
host="your-elasticache-endpoint",
port=6379,
ssl=True,
retry_on_timeout=True,
)
# 用途別 TTL 基準値 (秒)
BASE_TTL = {
"static": 3600,# 静的データ (設定・テンプレート): 1時間
"dynamic": 300,# 動的データ (プロファイル・在庫): 5分
"session": 1800,# セッション: 30分
}
def cache_set(key: str, value: str, data_type: str = "dynamic") -> None:
base = BASE_TTL.get(data_type, 300)
jitter = random.randint(0, base // 10) # ±10% ランダム揺らぎで Stampede 防止
r.set(key, value, ex=base + jitter)
def cache_get_or_set(key: str, fetch_fn, data_type: str = "dynamic") -> dict:
cached = r.get(key)
if cached:
return json.loads(cached)
value = fetch_fn() # OriginDB へのフォールバック
cache_set(key, json.dumps(value), data_type=data_type)
return value
# 使用例
cache_set("user:profile:1001", user_json,data_type="dynamic")# TTL: 300〜330秒
cache_set("config:theme:main", config_json, data_type="static") # TTL: 3600〜3960秒
cache_set("session:token:abc", session_json, data_type="session") # TTL: 1800〜1980秒
Jitter によりキー群の期限切れが分散され、Cache Stampede を根本的に抑制できる。cache_get_or_set パターンと組み合わせることで、キャッシュミス時のOriginDB負荷も最小化できる。
Q3. キャッシュキー衝突で意図しない上書き
アンチパターン
# NG: テナント識別子なし → マルチテナント環境でデータ混入
r.set("user:1001", json.dumps({"name": "Alice", "plan": "premium"}))
# 別テナントの同一IDが上書き → Alice のデータが消える
r.set("user:1001", json.dumps({"name": "Bob","plan": "free"}))
result = r.get("user:1001") # {"name": "Bob", "plan": "free"} が返る (Alice のデータ消失)
テナント・環境の識別子がないキー設計では、マルチテナント SaaS やステージング環境共有時に
異なるテナントの同一IDが衝突し、データ混入・情報漏洩 のリスクになる。
デバッグ困難な上、本番障害では顧客への説明責任が生じる深刻な問題となる。
正解パターン: Namespace prefix 設計
キー形式: {env}:{tenant}:{resource_type}:{id}
例: prod:tenant-a:user:1001、stg:tenant-b:product:2001
import redis
import json
from typing import Optional, Any
class PrefixedCache:
"""テナント分離・環境分離を保証するキャッシュヘルパー"""
def __init__(self, client: redis.Redis, env: str, tenant: str) -> None:
self._r = client
self._prefix = f"{env}:{tenant}"
def _key(self, resource_type: str, resource_id: str) -> str:
return f"{self._prefix}:{resource_type}:{resource_id}"
def set(self, resource_type: str, resource_id: str,
value: Any, ex: int = 300) -> None:
self._r.set(self._key(resource_type, resource_id), json.dumps(value), ex=ex)
def get(self, resource_type: str, resource_id: str) -> Optional[Any]:
raw = self._r.get(self._key(resource_type, resource_id))
return json.loads(raw) if raw else None
def delete(self, resource_type: str, resource_id: str) -> None:
self._r.delete(self._key(resource_type, resource_id))
# テナント別インスタンス生成
cache_a = PrefixedCache(r, env="prod", tenant="tenant-a")
cache_b = PrefixedCache(r, env="prod", tenant="tenant-b")
cache_a.set("user", "1001", {"name": "Alice", "plan": "premium"})
# 実際のキー: prod:tenant-a:user:1001
cache_b.set("user", "1001", {"name": "Bob", "plan": "free"})
# 実際のキー: prod:tenant-b:user:1001 ← 異なるキーで衝突ゼロ
assert cache_a.get("user", "1001")["name"] == "Alice" # 正常: データ混入なし
assert cache_b.get("user", "1001")["name"] == "Bob" # 正常: データ混入なし
Namespace prefix を強制するヘルパークラスを共通モジュールとして定義し、
全サービスで利用することでキー設計ミスを構造的に防ぐことができる。
Q4. DAX全テーブル適用で過剰コスト
アンチパターン
全 DynamoDB テーブル (30テーブル) を一律 DAX 経由でアクセスする設計。
コスト試算
| 項目 | 値 | 備考 |
|---|---|---|
| DAXクラスター | dax.r5.large × 3ノード | 最小推奨構成 |
| 月額コスト | 約 $194/月 | us-east-1 オンデマンド価格 |
| 実際の高頻度テーブル | 3テーブル (全体の10%) | 残り27テーブルはほぼ低頻度 |
| ROI | マイナス | 大半のDAXコストが無駄 |
書き込み中心のテーブルや低頻度アクセステーブルにDAXを適用しても、
キャッシュヒット率が低いため コストだけが増加 する。
正解パターン: 分析 → 選択的適用
Step 1: CloudWatch で全テーブルの読み取り消費量を分析
# 過去7日間の全テーブル ConsumedReadCapacityUnits を一覧取得
for table in $(aws dynamodb list-tables --query 'TableNames[]' --output text); do
total=$(aws cloudwatch get-metric-statistics \
--namespace AWS/DynamoDB \
--metric-name ConsumedReadCapacityUnits \
--dimensions Name=TableName,Value="$table" \
--start-time "$(date -u -d '-7 days' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || \
date -u -v-7d +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 604800 \
--statistics Sum \
--query 'Datapoints[0].Sum' \
--output text)
echo "$total $table"
done | sort -rn | head -5
Step 2: Top-3 高頻度テーブルのみ DAX 適用、残りは DynamoDB 直接接続
import boto3
import amazondax
import json
# DAX クライアント (高頻度テーブル用)
dax = amazondax.AmazonDaxClient(
endpoints=["prod-dax.xxxxxx.dax-clusters.ap-northeast-1.amazonaws.com:8111"]
)
# DynamoDB クライアント (低頻度テーブル用)
ddb = boto3.resource("dynamodb", region_name="ap-northeast-1")
# CloudWatch 分析結果: Top-3 高頻度テーブルのみ DAX 適用
DAX_TABLES = {"orders", "product_catalog", "user_sessions"}
def get_item(table_name: str, key: dict) -> dict:
if table_name in DAX_TABLES:
resp = dax.get_item(TableName=table_name, Key=key)
return resp.get("Item", {})
return ddb.Table(table_name).get_item(Key=key).get("Item", {})
Top-3テーブルへの選択的DAX適用でキャッシュヒット率を80%以上に維持しつつ、
コストを全テーブル適用比で 約70%削減 できる。
DAX の導入前後は CloudWatch CacheHits / CacheMisses メトリクスで効果を定量評価すること。
- DAXの費用対効果はテーブルごとに異なる — 全適用は非推奨
- まずCloudWatchで全テーブルのRCU消費量を計測し、上位3テーブルを特定してからDAX適用を検討する
- DAX clientとDynamoDB clientを用途別に使い分けるコードパターンが本番設計の基本
Q5. Memcached on 大量データで OOM多発
アンチパターン
# Memcached の Eviction 多発を確認 (本番ノードで実行)
echo "stats" | nc your-memcached-endpoint.cfg.use1.cache.amazonaws.com 11211 \
| grep -E "evictions|curr_items|bytes|limit_maxbytes"
# 出力例 (OOM状態):
# STAT limit_maxbytes 3254779904 (3.03GB — ノード上限)
# STAT bytes 3251000000 (3.02GB — ほぼ満杯)
# STAT curr_items 412800
# STAT evictions 284729 ← 大量Evictionで有効キャッシュが消滅
Memcached はインメモリ専用でメモリが上限に達すると LRU Eviction を実行する。
10GBのデータセットを cache.m7g.large (3.09GB) に詰め込むと、
有効キャッシュが次々と追い出されキャッシュヒット率が急落 する。
Memcached にはクラスターモード・レプリケーション・永続化がなく、
ノード障害でデータが全消失する点も本番リスクになる。
正解パターン Option A: Redis Cluster Mode + Data Tiering (cache.r6gd)
# OK: SSD階層化でメモリを拡張 — 冷データを自動的にSSDへオフロード
resource "aws_elasticache_replication_group" "redis_dt" {
description = "Redis with Data Tiering"
replication_group_id = "prod-cache-dt"
node_type= "cache.r6gd.xlarge"
num_cache_clusters= 3
automatic_failover_enabled = true
multi_az_enabled = true
data_tiering_enabled = true # ホットデータ→DRAM / コールドデータ→SSD
at_rest_encryption_enabled = true
transit_encryption_enabled = true
}
data_tiering_enabled = true で「ホットデータ → DRAM」「コールドデータ → SSD」に
自動階層化し、実効メモリ容量を大幅拡張できる。アクセスパターンに偏りがある場合に最適。
正解パターン Option B: Redis Cluster Mode Enabled + シャード追加
# OK: シャード数を増やして総メモリをスケールアウト
resource "aws_elasticache_replication_group" "redis_cme" {
description = "Redis Cluster Mode Enabled"
replication_group_id = "prod-cache-cme"
node_type= "cache.r7g.large"
num_node_groups= 4# 総メモリ ≒ 13.07GB × 4 = ~52GB
replicas_per_node_group = 2
automatic_failover_enabled = true
multi_az_enabled = true
at_rest_encryption_enabled = true
transit_encryption_enabled = true
}
Memcached → Redis 移行の主要注意点
| 移行ポイント | 対応内容 |
|---|---|
| データ型変換 | Memcached は文字列のみ → Redis の Hash/List/Set/SortedSet に再設計 |
| TTL 挙動差異 | Memcached の exptime → Redis の EX / EXAT オプションに読み替え |
| 接続文字列 | Memcached の Auto-Discovery endpoint → Redis の Cluster endpoint に変更 |
| クライアントライブラリ | pymemcache → redis-py (cluster_mode=True) に変更 |
| カットオーバ | Blue/Green デプロイで旧 Memcached → 新 Redis を段階的に切り替え |
8. まとめ — Database三部作完結 + 全17軸クロスリンク
8-1. Database三部作完結サマリ
本シリーズ3冊で AWS データベース本番運用の全レイヤーを体系的に網羅した。
| 巻 | 担当レイヤー | 主要カバー技術 | 設計テーマ |
|---|---|---|---|
| Vol1 | 主データストア層 | RDS / Aurora / DynamoDB | 永続化・整合性・スケーリング |
| Vol2 | 災対と移行層 | DMS / Aurora Global / Streams / Backup | DR・クロスリージョン・バックアップ |
| Vol3 (本記事) | キャッシュ層 | ElastiCache / DAX / MemoryDB | レイテンシ最小化・コスト最適化 |
三部作完結により、新規システム設計から障害対応・コスト最適化まで
Database 全域を一貫した設計思想で構築できる基盤が整った。
- Vol1 で設計した主データストアの整合性モデルを土台に
- Vol2 で策定した DR 方針・バックアップ戦略で耐障害性を担保し
- Vol3 でキャッシュ層を追加してレイテンシを最小化する
この三層アーキテクチャが AWS データベース本番運用の完成形となる。

8-2. Vol3 設計3原則
- 原則1: Multi-AZ + Auto-Failover を本番デフォルト
ElastiCache・MemoryDB ともautomatic_failover_enabled = true/multi_az_enabled = trueを必須設定とし、AZ障害時の自動昇格で RTO を最小化する。 - 原則2: TTL階層設計 + Cache Stampede対策で OriginDB保護
静的3600s / 動的300s / セッション1800s の3階層 + Jitter でキャッシュ一斉期限切れを防ぎ、OriginDB (RDS/DynamoDB) へのスパイクを遮断する。 - 原則3: 用途別キャッシュ選定 (ElastiCache / MemoryDB / DAX の使い分け)
Volatile キャッシュ → ElastiCache Redis、Durable (Redis API + AOF) → MemoryDB for Redis、DynamoDB 高速化 → DAX の3択で最適な永続性・コスト・レイテンシを実現する。
8-3. キャッシュ本番運用 落とし穴10選
- Single-AZ ElastiCache →
multi_az_enabled = true+ Auto-Failover 必須 - TTL未設定 → 用途別TTL階層設計 (静的3600s / 動的300s / セッション1800s)
- Cache Stampede未対策 → Jitter付きTTL + Probabilistic Early Expiration
- Cold Start未考慮 → Warm-up戦略: デプロイ後に主要キーを事前ロード
- キャッシュキー衝突 → Namespace prefix設計:
{env}:{tenant}:{type}:{id} - DAX全テーブル適用 → CloudWatch分析でTop-3高頻度テーブルのみ選択的適用
- Memcached on 大量データ → Redis Cluster Mode / Data Tiering へ移行
- Backup未設定 → MemoryDB: Multi-AZ Snapshot / ElastiCache: 自動バックアップ有効化
- TLS/RBAC未設定 →
transit_encryption_enabled = true+ User/ACL を本番デフォルト有効化 - CloudWatch監視未設定 → Evictions / CPUUtilization / SwapUsage / CacheHitRate をアラーム化
8-4. 全17軸 双方向ハブナビゲーション
- IAM Vol1 (Policy Design) ← ElastiCache User/User Group / DAX IAM Role
- EKS Vol1
- 復旧 Vol1
- AI Vol1 Bedrock Agents統合入門
- セキュリティ Vol1 ← TLS in-transit / RBAC認証
- コスト Vol1 ← Reserved Node Pricing / Data Tiering
- マルチアカウント Vol1
- Observability Vol1 ← Evictions/CPUUtilization監視
- Network Vol1 ← VPC Endpoint / Cross-VPC Cache
- Network Vol2 マルチアカウント網 ← TGW Route Table分離・PrivateLink・Direct Connect Resilient構成
- DevOps Vol1
- Database Vol1 (RDS×Aurora×DynamoDB) ← 主データストア層
- Database Vol2 (DMS×Aurora Global×Streams×Backup) ← 災対と移行層
- Serverless Vol1 ← Lambda + ElastiCache
- Serverless Vol2
- Container Vol1 / Vol2 EKS×ArgoCD GitOps編
- Storage Vol1
- Edge/CDN Vol1
- Analytics Vol1 ← Athena Query Result Cache
- ML/AI Vol2 ← Embedding cache統合