AWS Database入門Vol1|RDS×Aurora×DynamoDB本番運用

目次

Database本番運用入門 Vol1 — RDS × Aurora × DynamoDB

fig01: Database 3本柱 全体アーキテクチャ (RDS / Aurora / DynamoDB 選定マトリクス)

AWS本番運用 第11軸 Database本番運用 シリーズ起点 Vol1
本記事は AWS本番運用 全10軸 (IAM/EKS/復旧/AI/セキュリティ/コスト/マルチアカウント/Observability/Network/DevOps) を完遂した中堅エンジニアに向けた、第11軸 = Database本番運用 の起点記事です。RDS × Aurora × DynamoDB の3本柱 (RDB / 分散RDB / NoSQL) を全10軸統合視点で再統合し、本番品質の永続データ運用パターンを確立します。

前10軸シリーズ (22記事)

  • IAM Vol1-4 / EKS Vol1-3 / 復旧 Vol1-4 / AI Vol1-2 / セキュリティ Vol1-2 / コスト Vol1 / マルチアカウント Vol1 / Observability Vol1 / Network Vol1-2 / DevOps Vol1-2

関連シリーズ (Database 個別 deep-dive)

  • Aurora Backup/Restore/DR シリーズ (Aurora DR 限定 deep-dive)
  • Aurora No-Human Login (Aurora セキュリティ deep-dive)

1. なぜDatabase本番運用か — 全10軸からの架橋 + 第11軸結節点

全10軸 — IAM / EKS / 復旧 / AI / セキュリティ / コスト / マルチアカウント / Observability / Network / DevOps — を一通り完遂したエンジニアは、ある共通の「壁」に直面する。IAM で最小権限を徹底し、EKS でコンテナ基盤を整備し、復旧設計で RTO/RPO を定義し、Observability でメトリクスを可視化した — にもかかわらず、本番障害は止まらない。原因を追うと、多くが「永続データ層」の設計不備に行き着く。

Compute 層・Network 層・CI/CD 層をいくら磨いても、データストアの設計ミスは致命傷になる。コンテナが高速にスケールアウトしても、接続先の RDS が接続数限界に達すれば全リクエストがエラーになる。IaC で Backup ポリシーをコード化しても、PITR が無効のまま本番が走れば、誤削除から回復する手段がない。Multi-AZ を設定しているつもりが Single-AZ で稼働し続け、AZ 障害で数時間のダウンタイムを経験した事例は後を絶たない。

本記事 (Database本番運用 Vol1) は、全10軸を完遂した中堅エンジニアが次に直面する「データ永続化の固有ペイン」を、RDS × Aurora × DynamoDB の3本柱で体系的に解決するための起点記事だ。

Application 層だけでは不十分な理由

Application 層 (Compute / Network / CI/CD) は「処理」を担う。しかし「永続データ」には、Application 層とは本質的に異なる課題が存在する。以下の固有ペインは、アプリケーションコードの修正だけでは解決できない。

データ整合性の問題

Read Replica はデータを非同期で複製する。バッチ書き込みの直後に Read Replica から読み込むと、最大数十秒の遅延データを参照するリスクがある。アプリケーション側のキャッシュ戦略では対処しきれない、DB エンジン固有の遅延問題だ。RDS では ReplicaLag メトリクスを監視し、書き込み直後の参照は Primary に向けるルーティング設計が必要になる。

Backup と PITR の複雑性

S3 へのファイルバックアップと RDS の PITR (Point-In-Time Recovery) は根本的に異なる。RDS の PITR はトランザクションログをリプレイして任意の時刻に戻す仕組みであり、アプリケーションコードでは制御できない DB エンジン固有の機能だ。Backup の保持期間・Cross-Region コピー・KMS 暗号化の設定ミスは、リストア時に初めて発覚する。「Backup は設定している」と思っていても、保持期間が 1 日だったり KMS Key が Cross-Region でアクセス不可だったりするケースは多い。

Multi-AZ と Read Replica の混同

Multi-AZ は可用性 (HA) のための同期レプリカだ。障害時の自動フェイルオーバー専用であり、Standby インスタンスは通常の読み込みには使えない。一方 Read Replica は読み込みスケールアウトのための非同期レプリカだが、遅延が発生する。両者を混同して「Multi-AZ を設定しているから Read 負荷も分散されているはず」と誤解するチームは多い。この誤解が §3 で詳解する「Read Replica 遅延事故」の根本原因になる。

Aurora 固有の複雑性

Aurora は RDS とはアーキテクチャが根本的に異なる。Storage layer を Writer/Reader が共有し、Aurora Global Database でクロスリージョン複製を行う。しかし Global Database のフェイルオーバー時には Writer 昇格に時間がかかる可能性があり、その間のアプリケーション接続管理を誤ると書き込みデータが失われる。Aurora Serverless v2 では ACU (Aurora Capacity Unit) の min 値設定を誤るとコールドスタート遅延が発生し、本番リクエストが数秒タイムアウトする。

DynamoDB の NoSQL 設計パラダイム

RDB 設計者が DynamoDB を使い始めると、必ずといっていいほど Partition Key 設計で詰まる。Sequential な ID (Unix タイムスタンプ・AUTO_INCREMENT 代替のカウンタ等) を Partition Key に設定すると、新規書き込みが常に特定パーティションに集中し、Hot Partition による Throttle が発生する。DynamoDB のパーティションはハッシュ値で分散されるため、Sequential Key はハッシュが偏る。大量書き込みトラフィックで突然 ProvisionedThroughputExceededException が発生し、原因特定に時間がかかる。

本 Vol1 で得られる 4 つの成果

本記事を完遂することで、以下の 4 つの実践的能力を獲得できる。

成果 1: 3本柱選定能力

RDS / Aurora / DynamoDB の使い分け基準を、データモデル・スケーラビリティ・一貫性・運用負荷の 4 軸で定量的に判断できるようになる。「とりあえず RDS」「DynamoDB は大規模に向く」という曖昧な判断から脱却し、ワークロードの特性に応じて最適なデータストアを選定できる。

成果 2: RDS 実践力

Multi-AZ Deployment・Read Replica・PITR・Performance Insights・Secrets Manager 連携を Terraform で構築できる。接続数限界 (max_connections) の計算式と RDS Proxy による解決策を実装できる。Maintenance Window / Auto Minor Version Upgrade の本番安全設定を理解し、計画外の本番 Patch を防止できる。

成果 3: Aurora 実践力

Aurora クラスタの Writer/Reader 構成・Aurora Serverless v2 の ACU 設計・Aurora Global Database のフェイルオーバー手順を Terraform で実装できる。Backtrack (MySQL 限定) / Custom Endpoints / Parallel Query の適用場面を判断できる。既存の Aurora Backup/Restore/DR シリーズ・Aurora No-Human Login シリーズとの役割分担を理解した上で、本 Vol1 の学習範囲を正確に把握できる。

成果 4: DynamoDB 実践力

Partition Key・Sort Key・GSI の設計パターンを、アクセスパターン駆動で設計できる。On-Demand vs Provisioned の選定基準・PITR・DynamoDB Streams + Lambda 連携・Auto Scaling を Terraform で実装できる。Global Tables による Multi-Region 運用と DAX による Read 高速化の適用判断ができる。

既存シリーズとの住み分け

Database 関連の既存シリーズとの役割分担を明確にしておく。

Aurora Backup/Restore/DR シリーズ

Aurora のバックアップ戦略・Snapshot 管理・Cross-Region DR・Point-In-Time Restore の専門 deep-dive シリーズ。本 Vol1 では Aurora の全体像と基本的な Backup 設定を扱い、DR 専門の詳細内容 (RPO/RTO 設計・Snapshot Copy 自動化・DR リストア演習等) は既存シリーズ (Aurora Backup/Restore/DR) に委ねる。

Aurora No-Human Login シリーズ

Aurora の認証設計・IAM Database Authentication・Secrets Manager 連携・最小権限アクセス制御の専門 deep-dive シリーズ。本 Vol1 では Aurora のセキュリティ概要 (KMS 暗号化・Secrets Manager 基本設定) に触れ、認証の詳細設計は既存シリーズ (Aurora No-Human Login) に委ねる。

これらの既存シリーズは「Aurora 単体の深掘り」に特化しており、本 Vol1 は「3本柱 (RDS / Aurora / DynamoDB) の横断的な選定・実践」を扱う。用途が異なるため、両シリーズを参照することで全体像を網羅的に把握できる。

本番でよく起きる痛点 4 選

本 Vol1 の学習を進める前に、実際の本番環境でよく起きる痛点を把握しておこう。これらは各セクションで解決策とともに解説する。

痛点 1: Multi-AZ vs Read Replica の使い分けが曖昧

「Multi-AZ を設定しているから可用性は大丈夫」「Read Replica で読み込み負荷を分散している」— この 2 つを混同しているチームは多い。Multi-AZ の Standby は読み込みに使えない。障害時の自動フェイルオーバー専用だ。一方 Read Replica は読み込みに使えるが、非同期複製のため遅延がある。本番で「Read Replica から古いデータを読み込んでしまった」というトラブルの多くは、遅延を考慮しないアプリケーション設計に起因する。§3 で RDS Multi-AZ と Read Replica の違いを Terraform ベースで整理する。

痛点 2: Aurora Global Database でのハマりパターン

Aurora Global Database を設定しても、フェイルオーバー時にアプリケーションが旧 Region の Writer エンドポイントに接続し続けるケースがある。Global Database のフェイルオーバーは Aurora クラスタのフェイルオーバーとは異なり、手動での Writer 昇格操作が必要になることが多い。旧 Region への書き込みが継続するとデータが分岐し、収束が極めて困難になる。§4 で Global Database のフェイルオーバーシーケンスを詳解する。

痛点 3: DynamoDB Hot Partition 事故

テーブル設計時に Sequential な ID (Unix タイムスタンプ・AUTO_INCREMENT 代替のカウンタ等) を Partition Key に設定すると、新規書き込みが常に特定パーティションに集中する。DynamoDB のパーティションは Partition Key のハッシュ値で分散されるため、Sequential Key はハッシュが偏り、Hot Partition Throttle が発生する。大量書き込みトラフィックで突然 ProvisionedThroughputExceededException が発生し、本番サービスに影響が出る。§5 と §6 で Partition Key 設計の改善策を解説する。

痛点 4: Schema Migration の停止時間ペイン

RDS (MySQL/PostgreSQL) でカラム追加・インデックス作成・テーブル再構築を行う際、テーブルロックによるダウンタイムが発生する場合がある。特に数億行規模のテーブルでは pt-online-schema-change (MySQL) や pg_repack (PostgreSQL) なしには本番無停止移行が困難だ。Aurora Fast DDL は一部の DDL のみをオンラインで実行できるが、適用範囲を正確に理解しないと本番時間に誤ったマイグレーションを実行する事故につながる。§6 でマイグレーション手法の選定基準を整理する。

痛点 5: Lambda × RDS の接続数枯渇

Lambda と RDS を直接接続すると、Lambda の同時実行数が増えるにつれて RDS の接続数が急増する。Lambda はリクエストごとに新しいプロセスを起動するため、同時実行 1,000 のとき最大 1,000 接続が RDS に張られる。RDS の max_connections は DB インスタンスサイズに依存し、db.t4g.micro では約 60 接続が上限だ。上限を超えると Too many connections エラーで新規リクエストが全滅する。本番で突然のトラフィックスパイク時にこのエラーが出て、RDS を再起動するまで復旧しなかったケースは多い。解決策は RDS Proxy (接続プーリング) だが、Proxy 自体の設定ミスでも問題が起きる。§3 と §6 で RDS Proxy を含む接続数管理の全体像を解説する。

本記事の対象読者と前提スキル

本 Vol1 は以下のスキルセットを前提として書かれている。

  • AWS 基礎: VPC / EC2 / S3 / IAM の基本操作が理解できていること
  • Terraform 基礎: resource / variable / output / module の基本構文を理解していること
  • RDB 基礎: SQL の基本 (SELECT / INSERT / UPDATE / DELETE / JOIN) と ACID トランザクションの概念を理解していること
  • 全10軸シリーズ: IAM Vol1-4 / EKS Vol1-3 / 復旧 Vol1-4 / AI Vol1-2 / セキュリティ Vol1-2 / コスト Vol1 / マルチアカウント Vol1 / Observability Vol1 / Network Vol1-2 / DevOps Vol1-2 を完遂済みであること (必須ではないが、文中の相互参照が理解しやすくなる)

DynamoDB に関しては NoSQL 未経験者でも §5 で丁寧に解説するため、RDB 経験があれば問題ない。

本記事の各セクション構成

本記事は §1 (本セクション) + §2 を概論、§3〜§5 を各サービス実践、§6〜§7 を詰まりポイント・演習、§8 をまとめとして構成する。

  • §1: 全10軸からの架橋・Database 固有ペイン・4つの成果・痛点5選 (本セクション)
  • §2: 3本柱定義・AWS Database サービスマップ・選定マトリクス・統合アーキテクチャ
  • §3: RDS 実践 — Multi-AZ × Read Replica × Backup × Patch × Performance Insights (山場1)
  • §4: Aurora 実践 — クラスタ構成 / Global Database / Serverless v2 (山場2)
  • §5: DynamoDB 実践 — Partition Key × GSI × Streams × PITR × Auto Scaling (山場3)
  • §6: 詰まりポイント7選 — Read Replica 遅延・Hot Partition・接続数・Migration 等
  • §7: アンチパターン → 正解パターン変換演習 (5件)
  • §8: まとめ + Vol2 予告 + 落とし穴10選 + 全10軸クロスリンク

各 §3〜§5 の実践パートは Terraform コードを中心に構成されており、手元の AWS 環境で実際に動かしながら学べる。§6・§7 は実際の本番トラブルをベースにした実践的な問題解決集だ。


2. AWS Database 3本柱整理 + サービスマップ

AWS が提供する Database サービスは多岐にわたるが、本番運用の中核となるのは 3本柱 — RDS (RDB) / Aurora (分散 RDB) / DynamoDB (NoSQL) — だ。本セクションでは3本柱の定義・AWS Database サービスマップ・選定マトリクス・統合アーキテクチャを整理し、§3〜§5 の実践パートへの橋渡しを行う。

3本柱はそれぞれ「既存 RDB ワークロードの移行先 / スケールアウト要件が高い高負荷 OLTP / スキーマフリーな大規模 KV アクセス」という明確に異なるニーズを解決するために設計されている。「何でもできる万能 DB」は存在しない — ワークロードの特性を見極め、適切な柱を選ぶことが本番品質の第一歩だ。

3 本柱の定義

第1本柱: RDB (リレーショナルデータベース) = Amazon RDS

Amazon RDS は、MySQL / PostgreSQL / Oracle / SQL Server / MariaDB の5エンジンをマネージドで提供するリレーショナルデータベースサービスだ。強い一貫性 (Strong Consistency) とトランザクション保証 (ACID) を備え、既存の SQL アプリケーションをほぼそのままリフト&シフトできる。マルチ AZ 構成による自動フェイルオーバー・Read Replica による読み込みスケールアウト・Performance Insights によるクエリ分析と、本番運用に必要な機能を標準で備える。EC2 インスタンス上での自己管理型 DB と比較して、OS パッチ・バックアップ・レプリケーション設定の運用負荷を大幅に削減できる。

第2本柱: 分散 RDB = Amazon Aurora

Amazon Aurora は MySQL 互換・PostgreSQL 互換の2フレーバーで提供される、AWS が独自設計した分散型 RDB だ。Storage layer と Compute layer が分離されており、Storage は 3 AZ に 6 コピー自動分散される。Write は最低 4/6 コピーへの書き込み完了で確定し、高い耐久性を維持しながら Write Throughput は標準 MySQL の最大5倍・PostgreSQL の最大3倍を発揮する。クラスタ構成 (Writer + 最大15 Reader) / Aurora Serverless v2 (0.5〜128 ACU のオートスケール) / Aurora Global Database (クロスリージョン複製 RPO < 1 秒) を選択肢として持ち、Read Intensive から Multi-Region Active-Active まで幅広いワークロードに対応する。

第3本柱: NoSQL = Amazon DynamoDB

Amazon DynamoDB は、Key-Value / Document モデルをサポートする完全マネージド NoSQL データベースだ。テーブルは Partition Key (+ オプションの Sort Key) で構成され、データは Partition Key のハッシュ値によって複数パーティションに自動分散される。Provisioned Capacity Mode では RCU/WCU を事前に指定し、On-Demand Mode では使用量に応じて自動スケールする。Single-digit millisecond レイテンシを任意のスケールで実現し、DAX (DynamoDB Accelerator) と組み合わせることで Read latency を マイクロ秒オーダーに短縮できる。

AWS Database サービスマップ

3本柱に加え、AWS は用途特化型の Database サービスを多数提供している。

RDB カテゴリ

サービスエンジン主な用途
Amazon RDS for MySQLMySQL 8.0 / 5.7汎用 OLTP・既存 MySQL 移行
Amazon RDS for PostgreSQLPostgreSQL 16/15/14汎用 OLTP・JSON/地理空間データ
Amazon RDS for OracleOracle EE/SE2Oracle ライセンス移行
Amazon RDS for SQL ServerSQL Server 2022〜2016SQL Server ライセンス移行
Amazon RDS for MariaDBMariaDB 10.11/10.6MySQL 互換・オープンソース重視

分散 RDB カテゴリ

サービス特徴主な用途
Aurora MySQL 互換MySQL 8.0/5.7 互換・最大5倍スループット高負荷 OLTP・Read スケールアウト
Aurora PostgreSQL 互換PostgreSQL 16/15/14 互換高負荷 OLTP・PostGIS 地理空間
Aurora Serverless v20.5〜128 ACU 自動スケール変動負荷・Dev/Staging 環境
Aurora Global Databaseクロスリージョン複製 RPO < 1 秒Multi-Region DR・グローバルサービス

NoSQL カテゴリ

サービスデータモデル主な用途
Amazon DynamoDBKey-Value / Document高 Throughput OLTP・セッション管理
Amazon DAXDynamoDB キャッシュRead heavy・マイクロ秒レイテンシ要件
DynamoDB StreamsChange Data CaptureLambda 連携・イベント駆動アーキテクチャ
Amazon ElastiCache (Redis)In-Memory KV / データ構造セッション/キャッシュ・リアルタイム排他制御
Amazon ElastiCache (Memcached)In-Memory KVシンプルキャッシュ・オブジェクトストア

特殊用途カテゴリ

サービスデータモデル主な用途
Amazon Neptuneグラフ (Gremlin/SPARQL)推薦エンジン・ソーシャルグラフ・ナレッジグラフ
Amazon DocumentDBDocument (MongoDB 互換)MongoDB 移行・JSON ドキュメント検索
Amazon Timestream時系列IoT センサーデータ・メトリクス時系列
Amazon KeyspacesWide-column (Cassandra 互換)Cassandra 移行・大規模 IoT
Amazon QLDB台帳 (Ledger)改ざん不能な監査ログ・金融トランザクション

選定マトリクス — 4 軸で決める 3 本柱

3本柱の選定は「データモデル × スケーラビリティ × 一貫性 × 運用負荷」の 4 軸で判断する。

RDSAuroraDynamoDB
データモデルリレーショナル (テーブル/JOIN)リレーショナル (テーブル/JOIN)Key-Value / Document (JOIN なし)
スケーラビリティ垂直スケール + Read Replica (水平読み)水平読み (最大15 Reader) + Global DB水平書き・読み (無制限スケール)
一貫性Strong Consistency (Primary) / Eventual (Replica)Strong Consistency (Writer) / Eventual (Reader)Eventual Consistency (デフォルト) / Strong (オプション)
運用負荷中 (Multi-AZ/Backup/Patch 管理)中低 (Storage 自動管理・高可用性内蔵)低 (フルマネージド・Auto Scaling)
コスト特性インスタンス時間課金インスタンス + I/O 課金Read/Write ユニット or On-Demand 課金
max QPS 目安〜数万 QPS (インスタンス依存)〜数十万 QPS (Reader Pool)数百万 WPS / 数千万 RPS
レイテンシ1〜10 ms (SSD)1〜5 ms (共有 Storage)1〜5 ms / μs (DAX)

判断フロー:

  1. JOIN が必要か? → Yes → RDS / Aurora を選ぶ。No → DynamoDB を検討
  2. スケールが予測可能か? → Yes → RDS (Provisioned)。No → Aurora Serverless v2 または DynamoDB On-Demand
  3. クロスリージョン HA が必要か? → Yes → Aurora Global Database または DynamoDB Global Tables
  4. 既存 MySQL/PostgreSQL 資産があるか? → Yes → RDS または Aurora (エンジン互換)。No → ワークロード特性で選択
  5. Read/Write Throughput が RDS 限界を超えるか? → Yes → Aurora Reader Pool または DynamoDB

3 本柱統合アーキテクチャ — Microservices での併用パターン

本番の Microservices アーキテクチャでは、RDS・Aurora・DynamoDB を単独で使うのではなく、サービス特性に応じて組み合わせるのが標準パターンだ。

(冒頭の図 fig01 参照 — Database 3本柱 全体アーキテクチャ + 選定マトリクス)

パターン 1: OLTP + セッション管理 (RDS + DynamoDB)

[API Gateway] → [Lambda / ECS]
  ├─ [RDS for PostgreSQL] : 注文・在庫・ユーザープロフィール (強一貫性必須)
  └─ [DynamoDB]  : セッション・カート・一時データ (高 Throughput・TTL 自動削除)

ユーザープロフィールや注文データは RDS (ACID 保証・JOIN) で管理し、セッションやカートは DynamoDB (高 Throughput・TTL 自動削除) で管理する。RDS と DynamoDB を役割分担することで、それぞれの強みを最大化できる。RDS には max_connections の上限があるため、Lambda から直接接続する場合は RDS Proxy を経由して接続プールを共有し、接続数を抑制する。

パターン 2: Read Heavy + グローバル対応 (Aurora + DynamoDB Global Tables)

[CloudFront] → [ALB] → [ECS/EKS]
 ├─ [Aurora (Reader Pool)] : 商品マスタ・在庫照会 (Read Heavy・結果整合性許容)
 ├─ [Aurora (Writer)] : 受注処理・決済 (Strong Consistency)
 └─ [DynamoDB Global Tables]: ユーザー設定・セッション (Multi-Region 同期)

Aurora Reader Pool で Read Throughput を水平スケールし、グローバルユーザー向けの低レイテンシ読み込みを Aurora Global Database + DynamoDB Global Tables で実現する。Reader エンドポイントへの接続は Aurora が自動でロードバランスするが、重要な Write 直後の Read は Writer エンドポイントに向けることで遅延データ参照を防げる。

パターン 3: イベント駆動 (DynamoDB Streams + Lambda)

[DynamoDB Table] → [DynamoDB Streams] → [Lambda]
├─ [RDS] : 集計・分析データを RDBMS に書き込み
├─ [S3]  : 変更履歴をオブジェクトストレージに保存
└─ [EventBridge Pipes] : 後続サービスに変更イベントを配信

DynamoDB Streams を使った Change Data Capture (CDC) パターンで、DynamoDB への書き込みをトリガーに Lambda が下流サービスへデータを伝播する。マイクロサービス間のデータ同期・監査ログ生成・集計処理に有効だ。Lambda の Batch Size と Bisect On Error の設定を誤ると、1 件の処理失敗で Batch 全体がリトライされ重複処理が発生するため注意が必要だ。

3 本柱の選択でよくある誤りと正解パターン

誤り 1: 全データを RDS に集約する

「RDB が一番使い慣れている」という理由で、セッション・カート・通知キュー・ログなど、あらゆるデータを RDS の単一テーブルに格納するアンチパターンがある。セッションデータは TTL による自動削除が必要であり、RDS では手動でのバッチ削除が必要になる。高頻度の KV アクセスは RDS の接続数とロック競合を増やす。セッション・カート・一時データは DynamoDB TTL で管理するのが正解だ。

誤り 2: DynamoDB を RDB の代替として使い JOIN を再現しようとする

DynamoDB は JOIN をサポートしない。「JOIN の代わりに複数のクエリを発行して Application 側で結合する」という設計は、ネットワーク往復コストと Application 層の複雑性を増大させる。JOIN が多用される関係データは RDS/Aurora で扱うべきだ。DynamoDB はアクセスパターンが明確で、1つのクエリで必要なデータが取得できる設計 (Single Table Design) に向いている。

誤り 3: Aurora をコスト削減目的で RDS の代替として使う

Aurora は RDS より高機能だが、コストも高い。特に Aurora の I/O 料金 (書き込み: $0.20/100万リクエスト、読み込み: $0.20/100万リクエスト) は、I/O 集約型ワークロードでコストが大幅に増加する。小規模かつ I/O が少ないワークロードでは RDS の方がコスト効率が高い。Aurora I/O Optimized モードは I/O 料金ゼロだが基本料金が約 30% 増加するため、I/O 比率が高い場合にのみ有効だ。

特殊用途 DB を選ぶべき境界線

3本柱以外の AWS Database サービスを選ぶべき場面を整理する。本 Vol1 では3本柱を深く扱うが、ワークロードによっては特殊用途 DB の方が適合することがある。判断基準を把握しておくことで、「3本柱で無理に解決しようとして複雑性を増やす」ミスを防げる。

グラフ構造: Amazon Neptune

推薦エンジン・ソーシャルグラフ・ナレッジグラフなど、エンティティ間の複雑な関係 (グラフ構造) を扱うワークロードに向く。RDS で JOIN の多段ネストを試みるより、Neptune の Gremlin クエリで「友人の友人」「関連商品」を効率的に走査できる。ただし RDB 設計者には学習コストが高く、グラフ構造が必須でない限り RDS/DynamoDB で代替できる場合も多い。

時系列: Amazon Timestream

IoT センサーデータ・システムメトリクス・アプリケーションイベントなど、Timestamp + Metric + Tag 形式の時系列データを大量に保存・分析するワークロードに向く。RDS でメトリクス時系列を管理すると、テーブル肥大化と古いデータ削除の管理が複雑になる。Timestream は時系列データの自動階層化 (メモリ層→磁気層) と TTL 削除を組み込みで提供し、Grafana との直接連携もサポートする。

台帳: Amazon QLDB

金融トランザクション・医療記録・コンプライアンス監査など、改ざん不能な変更履歴が必要なワークロードに向く。QLDB は追記専用 (Append-Only) の台帳構造を持ち、全データ変更のハッシュチェーンで整合性を証明できる。DynamoDB Streams + S3 でも監査ログを実装できるが、QLDB は改ざん不能性の証明が標準機能として組み込まれている。

3 本柱を支える周辺サービス

3本柱を本番運用する上で、セットで理解しておくべき周辺サービスがある。

サービス役割組み合わせる 3本柱
RDS Proxy接続プーリング。Lambda × RDS/Aurora の接続数枯渇を防ぐRDS / Aurora
Secrets ManagerDB 認証情報のローテーション自動化RDS / Aurora / DynamoDB
AWS DMS既存 DB からの移行 (同種・異種エンジン対応)RDS / Aurora / DynamoDB
AWS SCTSchema Conversion Tool。異種エンジン移行時のスキーマ変換RDS / Aurora
DAXDynamoDB 専用インメモリキャッシュ。Read latency を μs に短縮DynamoDB
ElastiCacheRDS/Aurora の前段キャッシュ。Redis/MemcachedRDS / Aurora
Performance InsightsDB ロードの可視化。Top SQL と Wait Events の分析RDS / Aurora
Enhanced Monitoring1秒粒度の OS メトリクス収集RDS / Aurora

これらの周辺サービスは §3〜§5 の実践パートで Terraform コードとともに解説する。特に RDS Proxy と Secrets Manager は Lambda × RDS 構成では必須コンポーネントであり、Terraform の aws_db_proxy リソースとして実装する。

3本柱 性能・コスト比較表

観点RDSAuroraDynamoDB
Throughput上限インスタンスサイズ依存 (db.r6g.16xlarge で最大)Reader 最大15台でスケール (Writer はインスタンス依存)事実上無制限 (Auto Scaling / On-Demand)
読込レイテンシ1〜10 ms (Primary) / Replica 遅延あり1〜5 ms (共有 Storage 高速読み)1〜5 ms (Provisioned) / μs (DAX)
書込一貫性Strong (Primary)Strong (Writer) / 6コピー確認後コミットEventual (デフォルト) / Strong (オプション・コスト2倍)
コスト最小構成db.t4g.micro Multi-AZ 約 $29/月〜db.t4g.medium Multi-AZ 約 $73/月〜On-Demand: 読み $0.25/100万RCU・書き $1.25/100万WCU
スケールの容易さRead: Read Replica 追加。Write: インスタンスサイズ変更のみRead: Reader 自動追加。Write: インスタンスサイズ変更Read/Write ともに Provisioned 値変更 or On-Demand で自動
適したワークロード既存 SQL 資産・JOIN 必須・Strong Consistency 必須高 Throughput OLTP・Multi-Region DR・Read Intensive高 QPS Key-Value・セッション・IoT・タイムライン

§2 まとめ — 3 本柱の選定を確定させて §3 へ

本セクション (§2) で整理した内容を1枚のチェックリストにまとめる。§3〜§5 の実践パートへ進む前に、自分のワークロードがどの3本柱に属するかを確定させておこう。

  • JOIN が必要か、ACID トランザクションが必要か → Yes → RDS または Aurora
  • Read Throughput が単一インスタンスの限界を超えるか → Yes → Aurora (Reader Pool) を検討
  • クロスリージョン低レイテンシ複製が必要か → Yes → Aurora Global Database または DynamoDB Global Tables
  • スキーマが固定していないか、Key-Value / Document アクセスが中心か → Yes → DynamoDB
  • 書き込みが毎秒数千〜数万以上か、スケールが予測できないか → Yes → DynamoDB On-Demand
  • セッション・カート・TTL 自動削除が必要なデータか → Yes → DynamoDB TTL

上記のチェックで複数の選択肢が残る場合は、§2 の「選定マトリクス」に戻ってコスト・レイテンシ・運用負荷の 3 軸で最終判断する。次の §3 (RDS 実践) では Multi-AZ × Read Replica × Backup を Terraform で実装する。

「3本柱すべてを使うアーキテクチャが正解なのか」という疑問が生まれる場合もある。答えは「ワークロードによる」だ。単純な CRUD アプリなら RDS 単体で十分であり、RDS + DynamoDB の組み合わせは「セッション管理が必要 + 本番 Read 負荷がある」ユースケースで有効になる。Aurora は RDS が限界に近づいた段階で検討するのが現実的なスケールアップパスだ。すべてのサービスを一度に導入しようとするよりも、ワークロードの成長に合わせて段階的に採用するアプローチが運用コストを最小化できる。


3. RDS 実践 ★山場1 — Multi-AZ × Read Replica × Backup × Patch × Performance Insights

3-1. RDS インスタンスタイプ選定

RDS のインスタンスタイプは「バースト型 / 汎用 / メモリ最適化 / 超大型」の4系統に分かれる。本番環境では CPU クレジット枯渇のリスクがあるバースト型 (db.t系) を避け、ワークロードに応じて db.m 系または db.r 系を選定することが鉄則だ。

タイプ代表クラス用途特徴
バースト型db.t4g / db.t3開発・検証・低負荷CPU クレジット制。本番非推奨
汎用db.m6i / db.m6g本番 Web・OLTPCPU/メモリ バランス型
メモリ最適化db.r6i / db.r6g大規模 OLTP / AnalyticsRAM 重視。Buffer Pool 最大化
超大型db.x2idn / db.x2iednSAP HANA / 超大型 DB数TB RAM。特殊用途

Graviton (db.m6g / db.r6g / db.t4g) の選定

ARM ベースの AWS Graviton3 プロセッサは Intel 比で最大 30% コスト削減を実現する。MySQL 8.0 / PostgreSQL 14+ では Graviton3 (db.m6g / db.r6g) を第一選択肢とし、ベンチマーク比較で問題なければ採用するアプローチが合理的だ。

3-2. Multi-AZ Deployment (Standby Replica / Synchronous replication / 自動Failover)

Multi-AZ は 「高可用性」のための機能であり、Read traffic 分散には使えない点を押さえておく必要がある。Primary インスタンスの全書き込みが Synchronous replication で Standby に転送され、Primary 障害時に自動的に Standby が昇格する。

fig02: RDS Multi-AZ + Read Replica フロー

自動 Failover の動作

sequenceDiagram
 participant App as アプリケーション
 participant DNS as Route53 (RDS Endpoint)
 participant Primary as RDS Primary (AZ-a)
 participant Standby as RDS Standby (AZ-b)

 Primary->>Standby: Synchronous replication (書き込みごと)
 Note over Primary: AZ-a 障害発生
 Primary--xDNS: 応答停止
 DNS->>Standby: DNS フェイルオーバー切替 (60-120秒)
 Standby->>Standby: Writer 昇格
 App->>DNS: 同じ Endpoint で再接続
 DNS->>Standby: 新 Primary (元 Standby) へルーティング
 Note over App,Standby: アプリ側の DB 接続タイムアウトを 120秒超に設定する

Failover 完了まで 60〜120 秒かかるため、アプリケーション側の DB 接続タイムアウト設定を 120 秒超にしておくことが重要だ。connect_timeout / read_timeout / wait_timeout の3つを適切に設定する。

Synchronous vs Asynchronous replication 比較

項目Multi-AZ Standby (Synchronous)Read Replica (Asynchronous)
レプリケーション方式同期 (書き込みごとに確認)非同期 (遅延あり)
データ損失リスクゼロ (RPO=0)あり (Replica lag 分)
Read traffic 分散不可可 (Reader endpoint 経由)
用途HA / 自動FailoverRead スケールアウト

3-3. Read Replica (Asynchronous replication / Cross-Region / Read traffic 分散)

Read Replica は Primary の書き込みを非同期でコピーし、SELECT クエリを分散させる機能だ。Asynchronous replication のため「Replica lag」が発生し、参照系アプリで古いデータを読む可能性がある点に留意する。

Cross-Region Read Replica

Cross-Region Read Replica を設定することで、別リージョンに読み取り専用クローンを持てる。リージョン障害時の読み取り継続に使えるほか、リージョンをまたいだ分析クエリのオフロードにも活用できる。

Reader Endpoint の活用

RDS クラスター (Multi-AZ DB Cluster) では Reader Endpoint が提供され、複数の Replica へのロードバランシングを自動化できる。アプリ側では書き込みを Writer Endpoint、読み取りを Reader Endpoint に向けることでトラフィックを分散する。

Read Replica の設定上限と昇格 (Promote)

MySQL / PostgreSQL では Primary インスタンスあたり最大 5 台の Read Replica を作成できる。各 Replica は独立した DB インスタンスとして動作し、いつでも Standalone Primary として「昇格 (Promote)」できる。DR 対策として Cross-Region Read Replica を別リージョンに配置しておき、Primary リージョン障害時に昇格させて読み書き対応インスタンスとして切り替える手法が広く採用されている。

CloudWatch メトリクス ReplicaLag の監視アラームを 5〜10 秒で設定し、バッチ処理等による lag 急増を早期検知することが重要だ。lag が大きい状態で Reader Endpoint に READ をルーティングすると古いデータを返すため、バッチ実行スケジュールと Maintenance Window を考慮した運用設計が不可欠だ。

3-4. 自動 Backup (PITR / 7-35 日保持 / Cross-Region Snapshot Copy)

RDS の自動バックアップは有効化すると、毎日の Full Snapshot + 5 分ごとのトランザクションログにより PITR (Point-In-Time Recovery) が可能になる。保持期間は 1〜35 日の間で設定可能で、本番環境では 7 日以上が推奨される。

Cross-Region Snapshot Copy

DR (災害復旧) 対策として、定期スナップショットを別リージョンにコピーする自動化を構築する。RDS イベント通知 + Lambda、または AWS Backup の Cross-Region copy rule で実現できる。RPO (目標復旧時点) はスナップショットコピー間隔に依存する。

PITR (Point-In-Time Recovery) の実行手順

PITR は既存インスタンスへの上書きではなく、必ず「新しい DB インスタンス」として復元される点を押さえておく。CLI での実行例を示す。

aws rds restore-db-instance-to-point-in-time \
  --source-db-instance-identifier myapp-production \
  --target-db-instance-identifier myapp-production-restored \
  --restore-time 2024-01-15T03:00:00Z \
  --region ap-northeast-1

復元後のインスタンスは本番と同じ Parameter Group / Security Group を手動で再適用する必要がある。接続先の切り替えには Route 53 レコードの更新またはアプリ側の接続設定変更で対応する。PITR テストを四半期ごとに実施し、実際に指定した時点のデータが復元できることを確認する運用を推奨する。

3-5. Parameter Group / Option Group (engine-specific tuning)

Parameter Group は DB エンジン固有のパラメータ (MySQL の innodb_buffer_pool_size / PostgreSQL の work_mem など) を管理するオブジェクトだ。デフォルトグループは変更不可のため、本番環境では必ずカスタムグループを作成して割り当てる。

Option Group は MySQL / Oracle 固有の追加機能 (MySQL memcached plugin / Oracle TDE など) を有効化するオブジェクトだ。PostgreSQL / MariaDB では Option Group は使われない。

MySQL チューニングの基本パラメータ

パラメータ推奨値説明
innodb_buffer_pool_sizeRAM の 75%InnoDB バッファプール。最重要パラメータ
max_connectionsインスタンスサイズに応じて接続数上限。Lambda 環境では RDS Proxy 推奨
slow_query_log1 (有効)スロークエリログ有効化
long_query_time1 (秒)1秒超のクエリをスロークエリとして記録

3-6. Patch Management (Maintenance Window / Auto Minor Version Upgrade)

RDS の OS/エンジンパッチは Maintenance Window 内で自動適用される。Maintenance Window は週1回の時間帯 (例: 日曜 02:00-03:00 JST) を指定し、その時間帯に再起動を伴うパッチが適用される。

Auto Minor Version Upgrade

auto_minor_version_upgrade = true を設定すると、マイナーバージョンの自動アップグレードが有効になる。セキュリティパッチを自動適用できる利点がある一方、意図しない再起動が発生するリスクもある。本番環境では Maintenance Window を低トラフィック時間帯に設定し、Multi-AZ 構成で Failover を活用したローリング適用を行う。

3-7. Performance Insights + Enhanced Monitoring

Performance Insights (DB load 可視化)

Performance Insights は DB load (DBL) を vCPU 数を基準に可視化するツールだ。db load by waits グラフで CPU / IO / Locks / Row Lock 等の Wait event 別に分解し、ボトルネックを特定できる。Top SQL タブで重いクエリを一覧できるため、チューニング対象を素早く絞り込める。

Enhanced Monitoring (1秒粒度 OS metrics)

Enhanced Monitoring を有効化すると、CloudWatch Logs に 1 秒粒度の OS メトリクス (CPU / Memory / FileSystem / Disk I/O / Network / Process list) が送られる。RDS のデフォルト CloudWatch メトリクスは 60 秒粒度だが、Enhanced Monitoring は 1〜60 秒粒度で取得できるため、スパイク性の問題を捕捉しやすい。

Terraform では monitoring_interval = 60 (秒) と monitoring_role_arn を設定する。Enhanced Monitoring 用の IAM ロールには AmazonRDSEnhancedMonitoringRole マネージドポリシーを付与する必要がある。CloudWatch Logs の /aws/rds/instance/<db-name>/enhanced-monitoring ロググループに出力されるため、CloudWatch Logs Insights で任意のクエリを実行できる。

Performance Insights vs Enhanced Monitoring の使い分け

ツール主な用途粒度保持期間 (デフォルト)
Performance InsightsSQL レベルのボトルネック特定1秒7日 (無料) / 2年 (有料)
Enhanced MonitoringOS レベルのリソース監視1〜60秒CloudWatch Logs 設定に依存

両ツールを組み合わせることで、「DB load 急増 → OS メトリクス確認 → 原因 SQL 特定」という一連の診断フローを効率化できる。

3-8. セキュリティ: IAM Database Authentication / KMS 暗号化 / Secrets Manager

IAM Database Authentication

iam_database_authentication_enabled = true を設定すると、DB ユーザーパスワードの代わりに IAM 認証トークン (15 分有効) でログインできる。IAM ロールを使うため、長期パスワードの管理が不要になる。MySQL 8.0 / PostgreSQL 15 以降で利用可能。

KMS 暗号化 (Storage Encryption)

storage_encrypted = true でストレージを AES-256 で暗号化する。カスタマー管理キー (CMK) を指定することで、鍵のローテーション管理を組織の KMS ポリシーに組み込める。一度作成した RDS インスタンスの暗号化設定は変更不可のため、初期設定で必ず有効化する。

Secrets Manager パスワードローテーション

manage_master_user_password = true (Terraform aws_db_instance 属性) を設定すると、Secrets Manager でマスターパスワードを管理し、自動ローテーションが有効になる。パスワードの定期更新が自動化され、ハードコードリスクを排除できる。

VPC Security Group による接続制限

RDS インスタンスへのアクセスは VPC Security Group で制限する。アプリサーバーの Security Group ID を Inbound ルールのソースに指定する「Security Group 参照」方式で、IP アドレス管理の手間を省きながら接続元を厳密に絞り込める。DB ポート (MySQL: 3306 / PostgreSQL: 5432) のみを許可し、Public アクセスは publicly_accessible = false で明示的に無効化する。踏み台サーバーや Session Manager 経由でのアクセスに限定することで、外部からの直接接続を完全に遮断する構成が推奨される。

3-9. Terraform 例: RDS 本番構成

resource "aws_db_subnet_group" "main" {
  name = "rds-subnet-group"
  subnet_ids = var.private_subnet_ids
}

resource "aws_db_parameter_group" "mysql8" {
  name= "mysql8-production"
  family = "mysql8.0"

  parameter {
 name  = "slow_query_log"
 value = "1"
  }
  parameter {
 name  = "long_query_time"
 value = "1"
  }
  parameter {
 name= "innodb_buffer_pool_size"
 value  = "{DBInstanceClassMemory*3/4}"
 apply_method = "pending-reboot"
  }
}

resource "aws_db_instance" "main" {
  identifier  = "myapp-production"
  engine= "mysql"
  engine_version = "8.0"
  instance_class = "db.r6i.xlarge"
  allocated_storage = 100
  storage_type= "gp3"
  storage_encrypted = true
  kms_key_id  = aws_kms_key.rds.arn

  db_name  = "myapp"
  username = "admin"
  manage_master_user_password = true  # Secrets Manager 自動管理

  db_subnet_group_name= aws_db_subnet_group.main.name
  parameter_group_name= aws_db_parameter_group.mysql8.name
  vpc_security_group_ids = [aws_security_group.rds.id]

  multi_az= true
  backup_retention_period= 7
  backup_window = "17:00-18:00"  # UTC (JST 02:00-03:00)
  maintenance_window  = "Sun:18:00-Sun:19:00"  # UTC (JST 03:00-04:00 日曜)
  auto_minor_version_upgrade= true
  deletion_protection = true

  performance_insights_enabled = true
  performance_insights_retention_period = 7
  monitoring_interval = 60
  monitoring_role_arn = aws_iam_role.rds_enhanced_monitoring.arn

  enabled_cloudwatch_logs_exports = ["error", "slowquery", "general"]

  iam_database_authentication_enabled = true

  tags = {
 Environment = "production"
  }
}

resource "aws_db_instance" "read_replica" {
  identifier = "myapp-production-replica"
  replicate_source_db = aws_db_instance.main.identifier
  instance_class= "db.r6i.large"
  storage_encrypted= true

  performance_insights_enabled = true
  monitoring_interval = 60
  monitoring_role_arn = aws_iam_role.rds_enhanced_monitoring.arn
}
【失敗事例】Read Replica 遅延でアプリが古いデータを参照する事故

バッチ処理実行中 (大量書き込み) に Read Replica の replication lag が 30 秒以上に膨らみ、Reader Endpoint 経由でクエリしたアプリが 30 秒前のデータを返した事故が多発する。ユーザー向けの更新処理後に即座に READ した結果が「更新前のデータ」を返すという症状で現れる。

  • 発生条件: バッチ大量書き込み中 + アプリが Read Replica に READ をルーティング
  • 対策1: CloudWatch メトリクス ReplicaLag を監視し、閾値 (例: 5秒超) でアラームを設定
  • 対策2: 書き込み直後の READ は必ず Writer Endpoint に向ける (アプリ側のルーティング設計)
  • 対策3: バッチ専用 Parameter Group で read_only = ON + Replica lag 監視を組み合わせる
【失敗事例】max_connections 設定漏れで Lambda burst 時に接続枯渇

Lambda 関数が突発的にスケールアウトした際、RDS の接続数上限 (max_connections) を超過し、新規接続が “Too many connections” エラーで失敗する事故が多発する。db.t3.micro の場合、デフォルト max_connections は 66 程度しかなく、Lambda が 100 並列実行すると瞬時に上限に達する。

  • 発生条件: Lambda + RDS 直接接続 + バースト時 (Concurrency 急増)
  • 対策1 (推奨): RDS Proxy を間に挟み、コネクションプーリングを委譲する。Lambda が直接 DB に接続するコネクション数を大幅に削減できる
  • 対策2: Parameter Group で max_connections を明示的に設定し、インスタンスサイズに合わせた上限を把握する
  • 確認コマンド: SHOW GLOBAL STATUS LIKE 'Threads_connected'; で現在の接続数を確認できる

4. Aurora 実践 ★山場2 — クラスタ構成 / Global Database / Serverless v2 / I/O最適化

4-1. Aurora アーキテクチャ — Storage / Compute 分離設計の革新

Aurora は MySQL/PostgreSQL API 互換を維持しつつ、従来 RDS とはまったく異なるアーキテクチャで設計されている。最大の特徴は Storage layer と Compute layer の完全分離だ。

Storage layer — 6 コピー × 3 AZ 自己修復型ストレージ

Aurora Storage は 1 クラスタにつき 3 つの AZ に均等分散された 6 コピーで書き込まれる。書き込み成功の Quorum 条件は 6 コピー中 4 コピーへの書き込み完了 (4/6)、読み込み成功条件は 3/6 だ。これにより、1 つの AZ 全損と追加 1 ノード障害が同時発生しても読み書きを継続できる耐障害設計となっている。

操作Quorum 条件耐えられる障害
Write4/6 成功1 AZ 全損 + 1 ノード障害
Read3/6 成功1 AZ 全損
自己修復バックグラウンド障害ノードの自動データ再配置

ストレージは 10 GiB 単位で最大 128 TiB まで自動拡張し、手動 resize は不要だ。Compute インスタンスを増減しても Storage は再配置されないため、Reader を追加するコストがほぼゼロに近い。

Compute layer — Writer / Reader 独立スケーリング

Compute layer は Writer (Primary) と Reader (Replica) で構成される。通常の RDS Read Replica は Binlog を経由した非同期レプリケーションで数秒〜数十秒の遅延が生じるが、Aurora Reader は Storage layer を Writer と共有する。Writer が書き込んだデータは Storage 側で即座に利用可能となり、Reader は Redo Log の適用を待たずに最新データを参照できる。これが Aurora の「replication lag がほぼゼロ」の根拠だ。

RDS との根本的な違い

比較軸RDSAurora
ストレージSingle-AZ EBS (Multi-AZ は Standby へ同期)6 コピー × 3 AZ 自動分散
Read ReplicaBinlog 非同期 (遅延あり)Storage 共有 (遅延ほぼゼロ)
Failover 時間60〜120 秒通常 30 秒以内
最大 Reader 数5 (MySQL)15
ストレージ上限64 TiB128 TiB

4-2. クラスタ構成 — Writer + Reader Pool + Auto Scaling

クラスタエンドポイントの種類

エンドポイント接続先用途
Cluster EndpointWriter のみDDL / DML (書き込み)
Reader EndpointReader 全台 (ラウンドロビン)SELECT (読み込み負荷分散)
Instance Endpoint特定インスタンスデバッグ・メンテナンス
Custom Endpoint任意の Reader サブセット用途別 Reader Pool 分割

Aurora クラスタは 最大 15 台の Reader インスタンスを追加でき、Reader Endpoint が自動的に負荷分散する。書き込みは必ず Cluster Endpoint 経由で Writer 1 台に集約する。

Auto Scaling Reader Pool

OLTP 型アプリの読み込みトラフィックが急増した場合、Auto Scaling Policy で Reader インスタンス数を自動増減できる。Scaling の対象メトリクスは RDSReaderAverageCPUUtilization (CPU 使用率) または RDSReaderAverageDatabaseConnections (接続数) から選択する。

resource "aws_rds_cluster" "aurora_cluster" {
  cluster_identifier = "prod-aurora-cluster"
  engine = "aurora-postgresql"
  engine_version  = "15.4"
  database_name= "appdb"
  master_username = "dbadmin"
  manage_master_user_password = true
  db_subnet_group_name  = aws_db_subnet_group.aurora.name
  vpc_security_group_ids= [aws_security_group.aurora.id]
  storage_encrypted  = true
  kms_key_id= aws_kms_key.aurora.arn
  backup_retention_period  = 7
  preferred_backup_window  = "03:00-04:00"
  deletion_protection= true

  tags = { Environment = "production" }
}

resource "aws_rds_cluster_instance" "writer" {
  identifier = "prod-aurora-writer"
  cluster_identifier  = aws_rds_cluster.aurora_cluster.id
  instance_class= "db.r7g.xlarge"
  engine  = aws_rds_cluster.aurora_cluster.engine
  engine_version= aws_rds_cluster.aurora_cluster.engine_version
  publicly_accessible = false
}

resource "aws_rds_cluster_instance" "reader" {
  count= 2
  identifier = "prod-aurora-reader-${count.index}"
  cluster_identifier  = aws_rds_cluster.aurora_cluster.id
  instance_class= "db.r7g.xlarge"
  engine  = aws_rds_cluster.aurora_cluster.engine
  engine_version= aws_rds_cluster.aurora_cluster.engine_version
  publicly_accessible = false
}

resource "aws_appautoscaling_target" "aurora_reader" {
  max_capacity = 8
  min_capacity = 1
  resource_id  = "cluster:${aws_rds_cluster.aurora_cluster.cluster_identifier}"
  scalable_dimension = "rds:cluster:ReadReplicaCount"
  service_namespace  = "rds"
}

resource "aws_appautoscaling_policy" "aurora_reader_cpu" {
  name= "aurora-reader-cpu-scaling"
  policy_type  = "TargetTrackingScaling"
  resource_id  = aws_appautoscaling_target.aurora_reader.resource_id
  scalable_dimension = aws_appautoscaling_target.aurora_reader.scalable_dimension
  service_namespace  = aws_appautoscaling_target.aurora_reader.service_namespace

  target_tracking_scaling_policy_configuration {
 predefined_metric_specification {
predefined_metric_type = "RDSReaderAverageCPUUtilization"
 }
 target_value = 70.0
  }
}

4-3. Aurora Global Database — Cross-Region 設計 × RTO/RPO

Aurora Global Database の構成要素

Aurora Global Database は 1 つの Primary Region + 最大 5 つの Secondary Region で構成される。レプリケーションは Binlog を使わず、Storage layer の Redo Log を直接転送する仕組みのため、通常 1 秒未満のレプリケーション遅延を実現する。

項目通常 AuroraAurora Global Database
リージョン数1最大 6 (Primary×1 + Secondary×5)
レプリケーションローカル Storage 共有Redo Log 専用リンク (< 1 秒)
RTO (計画切替)N/A< 1 分 (Managed Planned Failover)
RPON/A< 1 秒
Secondary への書き込みN/A不可 (Read-Only)

Managed Planned Failover vs Unplanned Failover

  • Managed Planned Failover: 計画的なリージョン切替。Primary から Secondary へ書き込みを段階的に移行し、切替時のデータロスをほぼゼロにする。定期演習の実施を強く推奨する。
  • Unplanned Failover: Primary Region 障害時の手動切替。Secondary の昇格処理 (通常 1 分以内) の後、旧 Primary を切り離して新しい Secondary として再参加させる手順が必要だ。
resource "aws_rds_global_cluster" "aurora_global" {
  global_cluster_identifier = "prod-global-aurora"
  engine  = "aurora-postgresql"
  engine_version= "15.4"
  database_name = "appdb"
  storage_encrypted= true
}

resource "aws_rds_cluster" "primary" {
  cluster_identifier  = "prod-aurora-primary"
  engine  = "aurora-postgresql"
  engine_version= "15.4"
  global_cluster_identifier = aws_rds_global_cluster.aurora_global.id
  db_subnet_group_name= aws_db_subnet_group.primary.name
  vpc_security_group_ids = [aws_security_group.primary.id]
  storage_encrypted= true
  kms_key_id = aws_kms_key.primary.arn
  deletion_protection = true

  provider = aws.primary_region
}

resource "aws_rds_cluster" "secondary" {
  cluster_identifier  = "prod-aurora-secondary"
  engine  = "aurora-postgresql"
  engine_version= "15.4"
  global_cluster_identifier = aws_rds_global_cluster.aurora_global.id
  db_subnet_group_name= aws_db_subnet_group.secondary.name
  vpc_security_group_ids = [aws_security_group.secondary.id]
  storage_encrypted= true
  kms_key_id = aws_kms_key.secondary.arn

  provider= aws.secondary_region
  depends_on = [aws_rds_cluster_instance.primary_writer]
}

fig03: Aurora クラスタ + Global Database フロー

sequenceDiagram
  participant App as Application
  participant PE as Primary Cluster Endpoint<br>(us-east-1)
  participant PS as Primary Storage<br>(6コピー×3AZ)
  participant RS as Secondary Storage<br>(ap-northeast-1)
  participant SE as Secondary Cluster Endpoint

  App->>PE: Write (DML)
  PE->>PS: Quorum Write (4/6)
  PS-->>RS: Redo Log 転送 (< 1秒)
  RS-->>SE: Read 反映

  Note over PE,PS: Primary Region 障害発生
  App->>SE: Managed Failover 発動
  SE->>RS: Secondary → Primary 昇格 (< 1分)
  App->>SE: Write (DML) 再開 (新 Primary)

4-4. Aurora Serverless v2 — ACU 設計と本番適用

ACU (Aurora Capacity Unit) の定義

1 ACU = 約 2 GiB RAM + 比例した vCPU リソース。min_capacitymax_capacity の範囲内でトラフィックに応じて秒単位でスケールする。Serverless v1 ではアイドル後の初回クエリで「コールドスタート」遅延が問題だったが、Serverless v2 はインスタンスを完全停止せず min ACU 0.5 で常時待機するため、コールドスタートが発生しない。

設定項目説明推奨値
min_capacity最小 ACU (0.5 単位で設定可)本番: 2.0 以上、開発: 0.5
max_capacity最大 ACU (最大 128)ピーク RPS に合わせて設定

ユースケース別 ACU 設計

ユースケースmin ACUmax ACU理由
開発/ステージング0.54コスト最小化
本番 OLTP2.064スパイクトラフィック対応
バッチ夜間処理0.5128夜間のみ最大スペック活用
resource "aws_rds_cluster" "serverless_v2" {
  cluster_identifier = "prod-aurora-serverless-v2"
  engine = "aurora-postgresql"
  engine_version  = "15.4"
  database_name= "appdb"
  master_username = "dbadmin"
  manage_master_user_password = true
  db_subnet_group_name  = aws_db_subnet_group.aurora.name
  vpc_security_group_ids= [aws_security_group.aurora.id]
  storage_encrypted  = true
  deletion_protection= true

  serverlessv2_scaling_configuration {
 min_capacity = 2.0
 max_capacity = 64.0
  }
}

resource "aws_rds_cluster_instance" "serverless_v2_writer" {
  identifier= "prod-aurora-sv2-writer"
  cluster_identifier = aws_rds_cluster.serverless_v2.id
  instance_class  = "db.serverless"
  engine = aws_rds_cluster.serverless_v2.engine
  engine_version  = aws_rds_cluster.serverless_v2.engine_version
}

4-5. Parallel Query / Backtrack / Custom Endpoints — 上級機能活用

Parallel Query (Aurora MySQL 限定)

大量レコードの集計クエリを Storage layer の複数ノードで並列スキャンする機能だ。Compute layer でのフルスキャンが数十秒〜数分かかるクエリでも、Parallel Query を有効にすると Storage layer の分散処理により応答時間を大幅に短縮できる。DB Cluster Parameter Group で aurora_parallel_queryON に設定し、クエリ単位でヒント (/*+ NO_PARALLEL_QUERY */) により無効化もできる。Parallel Query は Aurora MySQL 3.x 系 (MySQL 8.0 互換) のみ対応し、Aurora PostgreSQL には存在しない。

I/O 最適化設定

Aurora の I/O 最適化モードは、ストレージへの不要な I/O を削減してコストを抑制する機能だ。DB Cluster Parameter Group の aurora_io_optimization_mode を設定することで I/O スループットを最適化できる。I/O 集約型のワークロードでは効果が顕著だが、クエリパターンにより効果は異なるため、Performance Insights で I/O wait を計測しながら調整する。

Backtrack (Aurora MySQL 限定)

データを誤って削除・更新した際に、スナップショット復元なしでクラスタを過去の時点まで巻き戻せる機能だ。バックトラックウィンドウを最大 72 時間設定でき、巻き戻し操作は数分以内で完了する。Snapshot Restore は新しいクラスタを立ち上げるため時間がかかるが、Backtrack は既存クラスタを直接巻き戻すため速い。Aurora PostgreSQL では利用できない点に注意が必要だ。

resource "aws_rds_cluster" "aurora_with_backtrack" {
  cluster_identifier = "prod-aurora-backtrack"
  engine = "aurora-mysql"
  engine_version  = "8.0.mysql_aurora.3.05.2"
  backtrack_window= 86400
  database_name= "appdb"
  master_username = "dbadmin"
  manage_master_user_password = true
  db_subnet_group_name  = aws_db_subnet_group.aurora.name
  vpc_security_group_ids= [aws_security_group.aurora.id]
  storage_encrypted  = true
  deletion_protection= true
}

Custom Endpoints — 用途別 Reader Pool 分割

Reader Endpoint はすべての Reader をラウンドロビンで使うが、Custom Endpoint を使うと 特定の Reader インスタンスだけを対象にしたエンドポイントを作成できる。例えば、高スペック Reader (OLAP 分析用) と標準スペック Reader (OLTP 読み込み用) を別 Custom Endpoint で分離し、アプリ側でエンドポイントを切り替えるだけで読み込み負荷のルーティングを制御できる。BI ツールや ETL バッチが OLTP の Reader と同居すると OLTP パフォーマンスに影響が出るため、Custom Endpoint による分離は本番環境での標準設計として組み込むことを推奨する。

事故事例: Aurora Global Database Failover 後のアプリ停止
Primary Region の AZ 障害で Aurora Global Database の Unplanned Failover を実行した際、Secondary Region の Writer 昇格 (約 40 秒) は完了したが、アプリ側の DB 接続先が旧 Primary の Cluster Endpoint のままだったため、旧クラスタへの接続が継続した。旧クラスタは Read-Only に降格しており Write が全て拒否され、約 10 分間のサービス停止が発生した。

根本原因: Failover 後のエンドポイント切替をアプリ設定・デプロイ手順に組み込んでいなかった。
対策: Route 53 CNAME または AWS Global Accelerator でエンドポイントを抽象化し、Failover 時に自動で新 Primary の Cluster Endpoint に切り替わる構成にする。Managed Planned Failover の切替手順を事前に文書化し、定期的に演習することが重要だ。詳細な DR 演習手順については Aurora × AWS Backup DR 演習完全ガイド を参照されたい。

詰まり: Serverless v2 min ACU 0.5 設定で起動遅延
開発環境で節約のために min_capacity = 0.5 に設定したところ、夜間のアイドル後に翌朝最初のクエリで 3〜5 秒の応答遅延が頻発した。Serverless v2 はコールドスタートがないとされるが、0.5 ACU は CPU リソースがほぼゼロに近いため、大きなクエリパースや接続確立に時間がかかる。

対策: 本番環境では min_capacity = 2.0 以上を推奨する。開発環境でも業務時間外のみ低 ACU にするスケジュール変更か、Serverless v2 ではなく固定インスタンス (db.t4g.medium) を採用するかを検討する。

既存 Aurora deep-dive 記事へのクロスリンク

本節では Aurora の基本構成と主要機能を解説した。DR 戦略とセキュリティ設計については専門記事で詳しく扱っている。本節の内容と組み合わせることで Aurora 本番運用の全体像が得られる。

Aurora 関連 deep-dive 記事


5. DynamoDB 実践 ★山場3 — Partition Key × GSI × Streams × PITR × Auto Scaling

DynamoDB は AWS が提供する完全マネージド NoSQL データベースサービスだ。サーバーレス設計により Throughput・Storage ともに自動スケールし、本番ワークロードの急激な流量変化に対応する。本節では Partition Key 設計から GSI・Streams・PITR・Auto Scaling・Global Tables・DAX まで体系的に解説する。

5-1. DynamoDB データモデル

DynamoDB は Key-Value / Document / Wide-Column の 3 つのデータモデルを単一サービスで実現する。

データモデル特徴代表的な用途
Key-ValuePrimary Key のみで値にアクセスセッション管理・キャッシュ
DocumentJSON ネスト構造をそのまま保持カタログ・ユーザープロファイル
Wide-ColumnSort Key で列方向に展開タイムライン・IoT 時系列

DynamoDB の最小データ単位は Item (行相当) で、Item は Primary Key と Attributes (JSON) で構成される。Primary Key 以外のスキーマは固定しない Schema-free 設計で、各 Item が異なる Attributes を持てる。

Primary Key の構成:
Simple Primary Key: Partition Key のみ。Key-Value アクセスに最適化
Composite Primary Key: Partition Key + Sort Key。Range クエリが可能

5-2. Partition Key 設計

Partition Key は DynamoDB が内部で使用する Hash 値でデータをパーティション分散する。設計の良否がスループットと Hot Partition 回避に直結する。

Cardinality (カーディナリティ) — Partition Key は値の種類が多いほど分散効率が高い。

良い Partition Key悪い Partition Key理由
user_id (UUID)status (“active”/”inactive”)UUID は高カーディナリティ・ステータスは 2 値のみ
order_id (ULID)created_date (日付文字列)ULID はランダム性あり・日付は 1 日 1 値
device_id#shard_Ntimestamp (sequential)shard サフィックス付加で分散強制

アクセスパターン駆動設計 — DynamoDB は SQL の JOIN に相当する機能を持たないため、取得クエリを先に列挙してから Primary Key・GSI を決定する。DynamoDB NoSQL Workbench でアクセスパターンを可視化してから設計を確定することを強く推奨する。

Composite Key パターン — 1 対多の関係を表現する場合、Partition Key に親エンティティ ID、Sort Key に子エンティティ ID + 属性を配置する。

PK: USER#u001
SK: ORDER#2026-05-01#o001
SK: ORDER#2026-05-02#o002
SK: PROFILE#v1

5-3. Sort Key 設計

Sort Key (Range Key) を設定すると、同一 Partition Key 内で Sort Key 値の Range クエリ が可能になる。

Sort Key を活用したクエリパターン (KeyConditionExpression の例):
begins_with('ORDER#') — カテゴリプレフィックスで階層データをフィルタ
between('ORDER#2026-05-01', 'ORDER#2026-05-31') — タイムスタンプ付き SK で月次期間クエリ

Sort Key 設計のベストプラクティス:
begins_with: TYPE#ENTITY_ID 形式のプレフィックスで複数エンティティを 1 テーブルに収容 (Single-Table Design)
between: ISO 8601 文字列 (2026-05-01T00:00:00Z) を SK に使うと辞書順 = 時系列順が成立
数値型 SK: 文字列と異なり数値昇順でソート。タイムスタンプを Unix epoch の数値で格納する場合は数値型 (N) を選択

5-4. Global Secondary Index (GSI) 設計

GSI は Primary Key とは異なる Key 構成で全 Item を検索可能にする Secondary Index だ。テーブルとは独立した Throughput を持ち、Eventually consistent read のみサポートする。

Sparse Index パターン — 特定の Attribute を持つ Item のみを GSI に含める手法。全 Item に当該 Attribute が存在しない場合、GSI は対象 Item のみを自動収容し Read/Write コストを最小化する。

global_secondary_index {
  name= "StatusIndex"
  hash_key  = "status"
  range_key = "created_at"
  projection_type = "INCLUDE"
  non_key_attributes = ["user_id", "order_total"]
  read_capacity= 5
  write_capacity  = 5
}

Projection 設定の選択:

Projection含まれる Attribute適用場面
KEYS_ONLYPK + SK + GSI Key のみ存在確認・主キー検索のみ
INCLUDE指定 Attribute を追加部分的な属性を GSI で返す
ALL全 AttributeGSI から完全な Item を返す (書き込みコスト大)

Eventual Consistency への注意: GSI への書き込み伝播は数秒の遅延が発生する。強整合性が必要なクエリは Primary Key アクセスを使用すること。

5-5. LSI (Local Secondary Index) との使い分け

項目GSILSI
Partition Key自由に変更可同じ Partition Key のみ
作成タイミングテーブル作成後でも追加可テーブル作成時のみ (変更不可)
Read ConsistencyEventually consistent のみStrongly consistent も選択可
上限20 GSI / テーブル5 LSI / テーブル
ThroughputGSI 独立の RCU/WCUTable と共有

使い分け原則: 同一 Partition Key 内の異なる Sort Key でのフィルタは LSI、Partition Key をまたぐ検索は GSI。LSI はテーブル作成後に追加できないため、初期設計でアクセスパターンを確定すること。

5-6. DynamoDB Streams — Change Data Capture

DynamoDB Streams は Item の変更 (INSERT / MODIFY / REMOVE) をリアルタイムに捕捉する CDC (Change Data Capture) 機能だ。

Stream View Type:
KEYS_ONLY: 変更 Item の Key のみ記録 (最小コスト)
NEW_IMAGE: 変更後の Item 全体
OLD_IMAGE: 変更前の Item 全体
NEW_AND_OLD_IMAGES: 変更前後の両方 (差分検出・監査に有用)

Lambda Streams Trigger 構成:

resource "aws_lambda_event_source_mapping" "streams" {
  event_source_arn = aws_dynamodb_table.main.stream_arn
  function_name = aws_lambda_function.streams_processor.arn
  starting_position= "LATEST"
  batch_size = 10
  maximum_batching_window_in_seconds = 5
  bisect_batch_on_function_error  = true

  destination_config {
 on_failure {
destination_arn = aws_sqs_queue.dlq.arn
 }
  }
}

bisect_batch_on_function_error = true を設定すると、バッチ処理失敗時にバッチを二分割して再試行し、毒メッセージ (Poison Pill) を自動特定する。失敗 Item は DLQ (Dead Letter Queue) へ転送して後続の運用調査に備える。

EventBridge Pipes 連携: Streams → EventBridge Pipes → EventBridge Bus へ接続すると、Lambda を介さずにマルチターゲット (Step Functions / Kinesis / SQS) へのルーティングが可能になる。

5-7. PITR / On-Demand Backup / Export to S3

バックアップ種別特徴用途
PITR (Point-In-Time Recovery)過去 35 日間の任意時点に復元誤削除・誤更新からの即時復旧
On-Demand Backup手動スナップショット。無期限保持リリース前の安全点・コンプライアンス
Export to S3S3 へ JSON/Ion 形式でエクスポート分析・アーカイブ・データ移行

PITR は有効化するだけで自動的に Continuous Backup が維持され、1 秒単位の粒度で過去 35 日以内の任意時点へ復元可能だ。復元先は 新規テーブル として作成されるため、本番テーブルへの影響ゼロで復旧作業が行える。本番環境では PITR を必ず有効化すること。

5-8. Auto Scaling — Target Tracking

DynamoDB Auto Scaling は Application Auto Scaling を内部利用し、RCU/WCU を自動調整する。

Target Tracking ポリシー設定:
TargetValue: 70.0 — 使用率 70% を維持目標 (突発スパイクへのバッファ 30% 確保)
ScaleOutCooldown: 60 秒 — スケールアウト後のクールダウン
ScaleInCooldown: 60 秒 — スケールイン後のクールダウン

Auto Scaling の応答には数十秒の遅延がある。予測可能なバースト (朝の一斉アクセス / バッチ起動前) は Scheduled Scaling で事前に Capacity を引き上げること。

5-9. On-Demand vs Provisioned — Capacity Mode 選定軸

項目On-DemandProvisioned + Auto Scaling
料金単位リクエスト課金 (RRU/WRU)キャパシティ課金 (RCU/WCU)
スケーリング即時自動 (制限なし)Auto Scaling (応答遅延あり)
Throttle リスクほぼなし急激なスパイク時にあり
コスト効率予測困難・低頻度に有利安定負荷で大幅コスト削減
切替制限On-Demand → Provisioned は 24h 1 回

選定基準:
– 新規サービス立ち上げ・トラフィックパターン未知: On-Demand
– 月間 RCU/WCU 消費量が予測可能・安定: Provisioned + Auto Scaling
– 定期バッチ + 通常トラフィック混在: Provisioned + Scheduled Scaling で事前 Capacity 拡張

5-10. Global Tables — Multi-Region multi-active レプリケーション

Global Tables は複数の AWS リージョンに同一テーブルを multi-active でレプリケートする機能だ。全リージョンで読み書き可能で、DynamoDB が競合を Last-Writer-Wins で自動解決する。

構成要件:
– DynamoDB Streams が有効 (Global Tables v2 は内部で Streams を使用)
– 全レプリカが同一テーブル名・同一 Primary Key スキーマ
– PITR は全リージョンで個別に有効化が必要

レプリケーション遅延: リージョン間は通常 1 秒未満。ネットワーク障害時はベストエフォート収束となるため、マルチリージョン読み書きが必要な場合はアプリ側で冪等性を確保すること。

5-11. DAX — DynamoDB Accelerator

DAX (DynamoDB Accelerator) はインメモリキャッシュクラスタで、DynamoDB Read レイテンシをミリ秒からマイクロ秒 (μs) に削減する。

特徴:
Item-level cache: GetItem の結果をキャッシュ。デフォルト TTL 5 分
Query/Scan cache: Query・Scan 結果をキャッシュ。デフォルト TTL 60 秒
Write-through: DAX への書き込みは DynamoDB へ同期転送
API 互換: DynamoDB SDK と同一インターフェース (エンドポイントを DAX クラスタへ変更するだけ)
VPC 内配置必須: パブリック IP を持たないため、同一 VPC の EC2 / Lambda から接続

適用場面:
– 読み取り集中 (Read Heavy) ワークロード
– 同一 Item への反復アクセス (人気商品ランキング・ゲームリーダーボード)
– レイテンシ要件がサブミリ秒 (広告入札系)

注意: DAX は Eventually consistent read のキャッシュのみ提供する。Strongly consistent read や TransactGet は DAX を経由せず DynamoDB へ直接転送される。

5-12. TTL — Time to Live

TTL は Item に自動削除期限を設定する機能だ。Unix エポック秒を格納した Attribute を TTL Key として指定すると、期限超過後 48 時間以内に DynamoDB が自動削除する。

resource "aws_dynamodb_table" "sessions" {
  name= "${var.project}-sessions"
  billing_mode = "PAY_PER_REQUEST"
  hash_key  = "session_id"

  attribute {
 name = "session_id"
 type = "S"
  }

  ttl {
 attribute_name = "expires_at"
 enabled  = true
  }

  point_in_time_recovery {
 enabled = true
  }
}

TTL 削除は Capacity を消費しない。削除イベントは DynamoDB Streams に REMOVE として記録されるため、期限切れセッションの後処理 (監査ログ書き込み・通知送信) を Lambda で実装できる。

5-13. セキュリティ — VPC Endpoint + Fine-grained Access Control (IAM Condition)

VPC Endpoint (Gateway 型): DynamoDB へのトラフィックをインターネットに露出せず AWS 内部ネットワークで完結させる。

resource "aws_vpc_endpoint" "dynamodb" {
  vpc_id= aws_vpc.main.id
  service_name= "com.amazonaws.${var.region}.dynamodb"
  vpc_endpoint_type = "Gateway"
  route_table_ids= [aws_route_table.private.id]

  policy = jsonencode({
 Version = "2012-10-17"
 Statement = [{
Effect = "Allow"
Principal = "*"
Action = ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query", "dynamodb:UpdateItem"]
Resource  = [aws_dynamodb_table.main.arn]
 }]
  })
}

Fine-grained Access Control (IAM Condition): dynamodb:LeadingKeys Condition Key を使用し、アクセス可能な Partition Key を認証済みユーザーの ID に限定する。

{
  "Effect": "Allow",
  "Action": ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query"],
  "Resource": "arn:aws:dynamodb:ap-northeast-1:123456789012:table/UserData",
  "Condition": {
 "ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
 }
  }
}

この設定により Cognito Identity の Sub に一致する Partition Key のみアクセス可能になり、行レベルセキュリティを IAM Condition だけで実現する。Lambda 関数に IAM Role を割り当て、同様の Condition を付与することで、バックエンドサービスでも Fine-grained 制御が適用できる。

5-14. Terraform 実装例 — aws_dynamodb_table / aws_dynamodb_table_replica / Lambda Streams Trigger

fig04: DynamoDB Partition Key + GSI 設計フロー

# DynamoDB テーブル (Provisioned + Auto Scaling + PITR + Streams + TTL + KMS)
resource "aws_dynamodb_table" "main" {
  name = "${var.project}-main"
  billing_mode  = "PROVISIONED"
  read_capacity = 5
  write_capacity= 5
  hash_key= "PK"
  range_key  = "SK"
  stream_enabled= true
  stream_view_type = "NEW_AND_OLD_IMAGES"

  attribute {
 name = "PK"
 type = "S"
  }
  attribute {
 name = "SK"
 type = "S"
  }
  attribute {
 name = "GSI1PK"
 type = "S"
  }
  attribute {
 name = "GSI1SK"
 type = "S"
  }

  global_secondary_index {
 name= "GSI1"
 hash_key  = "GSI1PK"
 range_key = "GSI1SK"
 projection_type = "INCLUDE"
 non_key_attributes = ["status", "created_at"]
 read_capacity= 5
 write_capacity  = 5
  }

  ttl {
 attribute_name = "expires_at"
 enabled  = true
  }

  point_in_time_recovery {
 enabled = true
  }

  server_side_encryption {
 enabled  = true
 kms_key_arn = aws_kms_key.dynamodb.arn
  }

  tags = var.common_tags
}

# Auto Scaling — Read Capacity
resource "aws_appautoscaling_target" "table_read" {
  max_capacity = 100
  min_capacity = 5
  resource_id  = "table/${aws_dynamodb_table.main.name}"
  scalable_dimension = "dynamodb:table:ReadCapacityUnits"
  service_namespace  = "dynamodb"
}

resource "aws_appautoscaling_policy" "table_read" {
  name= "DynamoDBReadCapacityUtilization"
  policy_type  = "TargetTrackingScaling"
  resource_id  = aws_appautoscaling_target.table_read.resource_id
  scalable_dimension = aws_appautoscaling_target.table_read.scalable_dimension
  service_namespace  = aws_appautoscaling_target.table_read.service_namespace

  target_tracking_scaling_policy_configuration {
 predefined_metric_specification {
predefined_metric_type = "DynamoDBReadCapacityUtilization"
 }
 target_value = 70.0
 scale_out_cooldown = 60
 scale_in_cooldown  = 60
  }
}

# Auto Scaling — Write Capacity
resource "aws_appautoscaling_target" "table_write" {
  max_capacity = 100
  min_capacity = 5
  resource_id  = "table/${aws_dynamodb_table.main.name}"
  scalable_dimension = "dynamodb:table:WriteCapacityUnits"
  service_namespace  = "dynamodb"
}

resource "aws_appautoscaling_policy" "table_write" {
  name= "DynamoDBWriteCapacityUtilization"
  policy_type  = "TargetTrackingScaling"
  resource_id  = aws_appautoscaling_target.table_write.resource_id
  scalable_dimension = aws_appautoscaling_target.table_write.scalable_dimension
  service_namespace  = aws_appautoscaling_target.table_write.service_namespace

  target_tracking_scaling_policy_configuration {
 predefined_metric_specification {
predefined_metric_type = "DynamoDBWriteCapacityUtilization"
 }
 target_value = 70.0
 scale_out_cooldown = 60
 scale_in_cooldown  = 60
  }
}

# Global Table レプリカ (ap-northeast-1 → us-east-1)
resource "aws_dynamodb_table_replica" "us_east_1" {
  global_table_arn = aws_dynamodb_table.main.arn
  region  = "us-east-1"
  point_in_time_recovery = true

  depends_on = [aws_dynamodb_table.main]
}

# Lambda DynamoDB Streams Trigger
resource "aws_lambda_event_source_mapping" "streams" {
  event_source_arn = aws_dynamodb_table.main.stream_arn
  function_name = aws_lambda_function.streams_processor.arn
  starting_position= "LATEST"
  batch_size = 10
  maximum_batching_window_in_seconds = 5
  bisect_batch_on_function_error  = true

  destination_config {
 on_failure {
destination_arn = aws_sqs_queue.dlq.arn
 }
  }
}
事故事例: Hot Partition Throttle (Sequential ID で 1 Partition に書き込み集中)
あるサービスで DynamoDB の Partition Key に created_at (タイムスタンプ文字列) を採用した結果、新規書き込みが常に最新タイムスタンプのパーティションに集中し、当該パーティション単独で Throttle が発生した。テーブル全体の WCU はまだ余裕があるにもかかわらず、パーティション上限 (3,000 WCU/秒) を超えた書き込みが全件 ProvisionedThroughputExceededException でリジェクトされ、注文データの書き込みが数分間停止した。

対処パターン:
1. Partition Key を UUID / ULID などランダム性のある ID に変更し、分散を均一化する
2. Write Sharding: Partition Key に shard_N サフィックス (ORDER#shard3) を付加して書き込みを複数パーティションへ意図的に分散する
3. DynamoDB NoSQL Workbench でアクセスパターンを可視化し、Key 設計レビューを実装前に必ず実施する

コスト落とし穴: GSI Projection ALL による書き込みコスト爆増
GSI の Projection を ALL に設定すると、テーブルの全 Attribute が GSI に複製される。Item に 50 Attribute が存在し、4 つの GSI すべてに ALL を設定している場合、1 回の PutItem で実質 5 倍 (Table + 4 GSI) の書き込みキャパシティを消費する。月 1 億 Item 書き込みのワークロードでは月間コストが単純計算で 5 倍になる。

対処パターン:
1. GSI Projection は KEYS_ONLY または INCLUDE (必要な Attribute のみ列挙) を原則とする
2. ALL を使用するのは GSI 経由で完全な Item を取得する必要がある場合のみ (その場合でも GSI 数を最小化する)
3. CloudWatch Metrics の ConsumedWriteCapacityUnits を GSI 別にモニタリングし、予算超過をアラートで即時検知する


6. 詰まりポイント7選 — Read Replica遅延 / Hot Partition / 接続数 / Backtrack / Throttle / Migration / Multi-Region

詰まり1: RDS Read Replica 遅延 — バッチ書き込み中に古いデータを参照する

発生パターン: 夜間バッチが Writer に大量 INSERT/UPDATE を実行中、同時にアプリが Read Replica から SELECT する。RDS Read Replica は非同期レプリケーション (Asynchronous replication) のため、Writer の更新が Replica に反映されるまでに数秒〜数十秒のラグが生じる。バッチ処理量が多い時間帯では ReplicaLag が 30 秒超になることもあり、アプリが古いレコードを参照して不整合処理を実行する事故につながる。

対処パターン:
1. CloudWatch の ReplicaLag メトリクスを監視し、閾値 (例: 10秒超) でアラームを発火する
2. アプリ側で「強一貫性が必要な読込 (決済確認・在庫引当等) は必ず Writer Endpoint に向ける」設計にする
3. バッチ実行時間帯を分析し、Replica が追いつく時間的余裕を設ける
4. Aurora に移行して Custom Endpoint で用途別の Reader Pool を分割する選択も有効

# CloudWatch: ReplicaLag アラーム
resource "aws_cloudwatch_metric_alarm" "replica_lag" {
  alarm_name = "rds-replica-lag-high"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name= "ReplicaLag"
  namespace  = "AWS/RDS"
  period  = 60
  statistic  = "Average"
  threshold  = 10
  alarm_description= "Read Replica lag exceeds 10 seconds"
  dimensions = {
 DBInstanceIdentifier = aws_db_instance.replica.identifier
  }
}

詰まり2: DynamoDB Hot Partition — Sequential Key で特定 Partition に集中

発生パターン: created_at (Unix timestamp) や order_id (連番) を Partition Key に使うと、新規書き込みが常に最新 Partition に集中する (Hot Partition)。DynamoDB は各 Partition に 1000 WCU / 3000 RCU の上限があるため、集中すると ProvisionedThroughputExceededException が発生し Throttle 状態になる。Adaptive Capacity が自動補正するが、対応に数分かかる場合もある。

対処パターン:
1. PK に UUID v4 (ランダム) を使い Partition 分散を確保する
2. 複合 PK: {entity_type}#{uuid} のようにプレフィックスを付けて意味を保ちながら分散する
3. 時系列アクセスが必要な場合は PK=user_id, SK=created_at の複合キー設計とし、PK を高カーディナリティ属性に保つ
4. Write Sharding: PK = base_key + "#" + random(0, N) で N 倍の書き込みスループットを確保し、読み込み側でスキャン・集約する

# DynamoDB: ランダムPKを持つテーブル設計例 (Hot Partition回避)
resource "aws_dynamodb_table" "orders" {
  name= "orders"
  billing_mode = "PAY_PER_REQUEST"
  hash_key  = "order_id"  # UUID v4 — ランダム高分散

  attribute {
 name = "order_id"
 type = "S"
  }

  # 時系列検索用 GSI: user_id (PK) + created_at (SK)
  global_secondary_index {
 name= "user-created-index"
 hash_key  = "user_id"
 range_key = "created_at"
 projection_type = "INCLUDE"
 non_key_attributes = ["status", "total_amount"]
  }

  attribute {
 name = "user_id"
 type = "S"
  }

  attribute {
 name = "created_at"
 type = "S"
  }

  point_in_time_recovery {
 enabled = true
  }
}

詰まり3: RDS 接続数限界 — Lambda burst で max_connections 超過

発生パターン: Lambda 関数が RDS に直接接続する構成で、同時実行数が急増すると「Too many connections」エラーが発生する。RDS の max_connections はインスタンスメモリに依存 (例: db.t4g.micro は約 60 接続) し、Lambda は 1 実行ごとに 1 接続を確立するため、同時実行 100 件で 100 接続を消費する。スパイクアクセスやバッチ処理との併用時に枯渇しやすい。

対処パターン:
1. RDS Proxy を配置して接続プーリングを確立する — Lambda は Proxy に接続し、Proxy は少数の永続接続で RDS と通信する
2. Proxy の max_connections_percent で RDS 接続数を制御する (例: 80% まで使用許可)
3. db.t4g → db.m6i など上位クラスに変更することで max_connections 上限を引き上げる一時対処も有効

# RDS Proxy 設定
resource "aws_db_proxy" "app_proxy" {
  name = "app-rds-proxy"
  debug_logging = false
  engine_family = "POSTGRESQL"
  idle_client_timeout = 1800
  require_tls= true
  role_arn= aws_iam_role.rds_proxy.arn
  vpc_security_group_ids = [aws_security_group.rds_proxy.id]
  vpc_subnet_ids= var.private_subnet_ids

  auth {
 auth_scheme = "SECRETS"
 description = "RDS Proxy auth via Secrets Manager"
 iam_auth = "REQUIRED"
 secret_arn  = aws_secretsmanager_secret.rds_credentials.arn
  }
}

resource "aws_db_proxy_default_target_group" "app_proxy" {
  db_proxy_name = aws_db_proxy.app_proxy.name

  connection_pool_config {
 connection_borrow_timeout = 120
 max_connections_percent= 80
 max_idle_connections_percent = 50
  }
}

詰まり4: Aurora Backtrack vs Snapshot Restore — 用途を混同すると復旧に失敗する

発生パターン: 誤データ投入事故が発生し「5分前の状態に戻したい」と Snapshot Restore を実行したところ、新クラスタ生成に 10 分かかり、さらにアプリの接続先変更作業が必要になって 30 分以上のダウンタイムが発生した。Backtrack を使えば 1〜2 分で元の状態に巻き戻せたのに、事前設定がなかったため使えなかった。

| 比較軸 | Backtrack | Snapshot Restore |
|——–|———–|—————–|
| 対象エンジン | Aurora MySQL のみ | 全エンジン |
| 保持期間 | 最大 72 時間 | 無制限 (手動管理) |
| 復旧時間 | 1〜2 分 | 5〜15 分以上 |
| エンドポイント変更 | 不要 (同クラスタ) | 必要 (新クラスタ) |
| 用途 | 誤 DML の高速巻き戻し | 長期保存・別環境復元 |

対処パターン:
1. Aurora MySQL クラスタには必ず Backtrack を有効化する (事前設定なしでは使えない)
2. target_backtrack_window を 86400 (24時間) 以上に設定する
3. Snapshot は Cross-Region コピーと KMS 暗号化を組み合わせて長期 DR 用途に使う

resource "aws_rds_cluster" "aurora" {
  cluster_identifier= "aurora-mysql-prod"
  engine= "aurora-mysql"
  engine_version = "8.0.mysql_aurora.3.04.0"
  master_username= "admin"
  manage_master_user_password = true

  # Backtrack 有効化 (24時間)
  backtrack_window  = 86400

  backup_retention_period = 14
  preferred_backup_window = "03:00-04:00"

  deletion_protection  = true
}

詰まり5: DynamoDB Throttle — Provisioned 超過と Auto Scaling 応答遅延

発生パターン: Provisioned キャパシティモードで RCU/WCU を固定設定しているテーブルに、セール期間などのスパイクアクセスが到来して Throttle が発生する。Auto Scaling は CloudWatch アラームを経由するため、トラフィック急増から Capacity 増加まで 2〜5 分のラグがある。この間、Throttle されたリクエストはアプリ側エラーとして返る。

対処パターン:
1. 予測できないスパイクには On-Demand モードに切り替える (数秒で Capacity 調整)
2. Provisioned を維持する場合は target_value を 50〜60% に抑えてバッファを確保する
3. Exponential Backoff + Jitter を実装してリトライ時の Throttle 連鎖を防ぐ
4. CloudWatch の ThrottledRequests メトリクスを監視してアラームを設定する

# DynamoDB Auto Scaling: 書き込みキャパシティ (60%ターゲット)
resource "aws_appautoscaling_target" "dynamodb_write" {
  max_capacity = 1000
  min_capacity = 10
  resource_id  = "table/${aws_dynamodb_table.orders.name}"
  scalable_dimension = "dynamodb:table:WriteCapacityUnits"
  service_namespace  = "dynamodb"
}

resource "aws_appautoscaling_policy" "dynamodb_write_policy" {
  name= "dynamodb-write-autoscaling"
  policy_type  = "TargetTrackingScaling"
  resource_id  = aws_appautoscaling_target.dynamodb_write.resource_id
  scalable_dimension = aws_appautoscaling_target.dynamodb_write.scalable_dimension
  service_namespace  = aws_appautoscaling_target.dynamodb_write.service_namespace

  target_tracking_scaling_policy_configuration {
 predefined_metric_specification {
predefined_metric_type = "DynamoDBWriteCapacityUtilization"
 }
 # 60%で拡張 → 40%バッファを確保
 target_value = 60.0
  }
}

詰まり6: Schema Migration — RDS / Aurora / DynamoDB で戦略が全く異なる

発生パターン: RDS PostgreSQL でカラム追加の ALTER TABLE を実行したところ、テーブル全体にロックがかかり 3 分間のクエリ待機が発生した。また DynamoDB で新しいアクセスパターンに対応する GSI を後から追加したところ、既存データのバックフィルに 40 分かかりアプリが Provisioned WCU を使い切った。

エンジン別 Schema Migration 戦略:

| エンジン | 操作 | ロック | 代替手段 |
|———|——|——–|———|
| RDS PostgreSQL | ALTER TABLE ADD COLUMN | テーブルロック | pg_repack でロックなし再構築 |
| RDS MySQL | ALTER TABLE | メタデータロックのみ (InnoDB) | pt-online-schema-change |
| Aurora MySQL | ALTER TABLE | Fast DDL (メタデータ変更のみ) | デフォルトで高速 |
| DynamoDB | GSI 追加 | なし (非同期バックフィル) | バックフィル中は WCU 消費増大 |

対処パターン:
1. RDS PostgreSQL は pg_repack をメンテナンスウィンドウで実行し、本番トラフィックへの影響を最小化する
2. DynamoDB GSI 追加時は CREATING 状態のモニタリングと WCU のバッファ確保を事前に行う
3. DynamoDB の大規模 Schema 変更 (PK変更等) は新テーブル作成→DynamoDB Streams で移行→DNS 切替のパターンを使う

# DynamoDB GSI 追加: バックフィル中の WCU 増強設定例
resource "aws_dynamodb_table" "events" {
  name  = "events"
  billing_mode= "PROVISIONED"
  write_capacity = 500
  read_capacity  = 100
  hash_key = "event_id"

  attribute {
 name = "event_id"
 type = "S"
  }

  attribute {
 name = "status"
 type = "S"
  }

  # 後から追加する GSI (バックフィル中はWCU消費増大)
  global_secondary_index {
 name= "status-index"
 hash_key  = "status"
 projection_type = "KEYS_ONLY"
 write_capacity  = 200
 read_capacity= 100
  }

  point_in_time_recovery {
 enabled = true
  }
}

詰まり7: Multi-Region 複製遅延 — Aurora Global DB / DynamoDB Global Tables の収束時間

発生パターン: Aurora Global Database で東京 (Primary) → シンガポール (Secondary) にデータを複製している構成で、東京リージョンへの大量書き込み直後にシンガポールから読み込むと古いデータが返ってきた。DynamoDB Global Tables でも同様に、複数リージョンへの同時書き込み後に Last-Write-Wins によって一方の書き込みが消滅する事故が発生した。

| 比較軸 | Aurora Global Database | DynamoDB Global Tables |
|——–|———————-|———————-|
| レプリケーション方式 | 非同期 (通常 < 1 秒) | 非同期 (通常 < 1 秒) |
| Failover RTO | < 1 分 (Managed Failover) | 自動 (リージョン障害時) |
| 書き込み競合 | 1 リージョンのみ書き込み可 (強一貫性) | Last-Write-Wins (競合解決なし) |
| 強一貫性保証 | Writer リージョンのみ | なし (Eventually consistent) |

対処パターン:
1. Aurora Global DB は 1 リージョンのみを Writer とし、他は Reader として使うことで書き込み競合を回避する
2. DynamoDB Global Tables では同一 item への複数リージョン同時書き込みを避ける設計 (ユーザー単位でリージョン固定等) を採用する
3. 強一貫性が必要なユースケースでは Global Tables を使わず、単一リージョン DynamoDB を採用する
4. Aurora Global Database の AuroraGlobalDBReplicationLag を定期監視してアラームを設定する

# Aurora Global Database + Secondary クラスタ
resource "aws_rds_global_cluster" "global" {
  global_cluster_identifier = "aurora-global-prod"
  engine  = "aurora-postgresql"
  engine_version= "15.4"
  database_name = "appdb"
  deletion_protection = true
}

resource "aws_rds_cluster" "secondary" {
  provider= aws.singapore
  cluster_identifier  = "aurora-secondary-sg"
  engine  = "aurora-postgresql"
  engine_version= "15.4"
  global_cluster_identifier = aws_rds_global_cluster.global.id

  db_subnet_group_name= aws_db_subnet_group.secondary.name
  vpc_security_group_ids = [aws_security_group.aurora_secondary.id]

  lifecycle {
 ignore_changes = [replication_source_identifier]
  }
}

7. アンチパターン→正解パターン変換演習 (5件)

▶ 演習5問の解答を確認する


演習1: 壊れた RDS Multi-AZ — Single-AZ で本番稼働している

Before (アンチパターン):

resource "aws_db_instance" "app_db" {
  identifier  = "app-db-prod"
  engine= "postgres"
  engine_version = "15.4"
  instance_class = "db.m6i.large"
  allocated_storage = 100

  # ❌ Multi-AZ 未設定 (default = false)
  # multi_az = false

  username = "dbadmin"
  manage_master_user_password = true
  db_subnet_group_name = aws_db_subnet_group.main.name
}

After (正解パターン):

resource "aws_db_instance" "app_db" {
  identifier  = "app-db-prod"
  engine= "postgres"
  engine_version = "15.4"
  instance_class = "db.m6i.large"
  allocated_storage = 100
  storage_encrypted = true
  kms_key_id  = aws_kms_key.rds.arn

  # ✅ Multi-AZ 有効化 (Standby Replica + 自動Failover)
  multi_az = true

  backup_retention_period= 14
  backup_window = "03:00-04:00"
  maintenance_window  = "sun:05:00-sun:06:00"
  auto_minor_version_upgrade = false
  deletion_protection = true

  username = "dbadmin"
  manage_master_user_password = true
  db_subnet_group_name  = aws_db_subnet_group.main.name
  vpc_security_group_ids= [aws_security_group.rds.id]
}

解説: multi_az = true を設定すると、RDS は別 AZ に Standby Replica を自動作成し、Primary 障害時に 60〜120 秒で自動 Failover する。未設定のまま Single-AZ で運用すると、AZ 障害や OS パッチ再起動の際に手動復旧が必要になる。本番 DB は必ず Multi-AZ を有効化すること。


演習2: 壊れた Aurora Reader Pool — Auto Scaling 設定漏れで Reader 1台のみ

Before (アンチパターン):

# ❌ Reader インスタンス 1台のみ / Auto Scaling なし
resource "aws_rds_cluster_instance" "reader" {
  count  = 1
  identifier= "aurora-reader-0"
  cluster_identifier = aws_rds_cluster.aurora.id
  instance_class  = "db.r6g.large"
  engine = aws_rds_cluster.aurora.engine
  engine_version  = aws_rds_cluster.aurora.engine_version
}
# Auto Scaling リソースなし → 読み込み負荷増大時に1台で詰まる

After (正解パターン):

# ✅ Reader 最小1台 + Auto Scaling (最大5台)
resource "aws_rds_cluster_instance" "reader" {
  count  = 1
  identifier= "aurora-reader-${count.index}"
  cluster_identifier = aws_rds_cluster.aurora.id
  instance_class  = "db.r6g.large"
  engine = aws_rds_cluster.aurora.engine
  engine_version  = aws_rds_cluster.aurora.engine_version
}

resource "aws_appautoscaling_target" "aurora_reader" {
  max_capacity = 5
  min_capacity = 1
  resource_id  = "cluster:${aws_rds_cluster.aurora.cluster_identifier}"
  scalable_dimension = "rds:cluster:ReadReplicaCount"
  service_namespace  = "rds"
}

resource "aws_appautoscaling_policy" "aurora_reader_cpu" {
  name= "aurora-reader-cpu-scaling"
  policy_type  = "TargetTrackingScaling"
  resource_id  = aws_appautoscaling_target.aurora_reader.resource_id
  scalable_dimension = aws_appautoscaling_target.aurora_reader.scalable_dimension
  service_namespace  = aws_appautoscaling_target.aurora_reader.service_namespace

  target_tracking_scaling_policy_configuration {
 predefined_metric_specification {
predefined_metric_type = "RDSReaderAverageCPUUtilization"
 }
 target_value = 70.0
 scale_in_cooldown  = 300
 scale_out_cooldown = 60
  }
}

解説: Aurora Reader Pool の Auto Scaling を設定しないと、読み込み負荷が急増した際に Reader 1 台で全ての SELECT を処理することになり、CPU が 100% に張り付いてクエリタイムアウトが多発する。scale_out_cooldown = 60 秒で素早く Reader を追加し、scale_in_cooldown = 300 秒でコスト効率よくスケールインする設定が基本パターン。


演習3: 壊れた DynamoDB Partition Key — timestamp で Hot Partition

Before (アンチパターン):

# ❌ created_at (Unix timestamp 連番) を PK に使用 → Hot Partition
resource "aws_dynamodb_table" "events_bad" {
  name= "events"
  billing_mode = "PAY_PER_REQUEST"
  hash_key  = "created_at"  # ❌ Sequential → 書き込みが最新Partitionに集中

  attribute {
 name = "created_at"
 type = "N"
  }
}

After (正解パターン):

# ✅ UUID v4 を PK に + 時系列検索は GSI で対応
resource "aws_dynamodb_table" "events_good" {
  name= "events"
  billing_mode = "PAY_PER_REQUEST"
  hash_key  = "event_id"# ✅ UUID v4 — ランダム高分散
  range_key = "created_at" # SK で時系列ソート

  attribute {
 name = "event_id"
 type = "S"
  }

  attribute {
 name = "created_at"
 type = "N"
  }

  attribute {
 name = "tenant_id"
 type = "S"
  }

  # テナント別時系列検索用 GSI
  global_secondary_index {
 name= "tenant-time-index"
 hash_key  = "tenant_id"  # ✅ テナントIDで分散
 range_key = "created_at"
 projection_type = "ALL"
  }

  point_in_time_recovery {
 enabled = true
  }

  tags = {
 Environment = "production"
  }
}

解説: created_at や連番 ID を PK に使うと、書き込みが常に「最新の Partition」に集中する。UUID v4 を PK にすることで 128 bit のランダム空間に Partition を均等分散できる。時系列検索が必要な場合は、高カーディナリティな tenant_id を GSI の PK に使い、created_at を Sort Key にすることで効率的な範囲クエリと分散を両立する。


演習4: 壊れた Backup Policy — PITR 無効 + Snapshot 7日保持のみ

Before (アンチパターン):

# ❌ PITR 無効 + 短い Snapshot 保持
resource "aws_db_instance" "app_db" {
  identifier  = "app-db-prod"
  engine= "postgres"
  engine_version = "15.4"
  instance_class = "db.m6i.large"
  allocated_storage = 100

  # ❌ 7日間のみ → 1週間前以前の任意時点に戻せない
  backup_retention_period = 7
  # PITR は RDS では自動有効 (backup_retention_period > 0 で有効)
  # DynamoDB は明示的に有効化が必要
}

# ❌ DynamoDB: PITR 無効
resource "aws_dynamodb_table" "orders" {
  name= "orders"
  billing_mode = "PAY_PER_REQUEST"
  hash_key  = "order_id"

  attribute {
 name = "order_id"
 type = "S"
  }
  # point_in_time_recovery なし → 誤削除時に復元不可
}

After (正解パターン):

# ✅ RDS: 35日 PITR + Cross-Region Snapshot
resource "aws_db_instance" "app_db" {
  identifier  = "app-db-prod"
  engine= "postgres"
  engine_version = "15.4"
  instance_class = "db.m6i.large"
  allocated_storage = 100
  storage_encrypted = true

  # ✅ 最大35日間 PITR (任意の秒単位で復元可能)
  backup_retention_period = 35
  backup_window  = "03:00-04:00"
  deletion_protection  = true
}

# ✅ DynamoDB: PITR 明示的有効化
resource "aws_dynamodb_table" "orders" {
  name= "orders"
  billing_mode = "PAY_PER_REQUEST"
  hash_key  = "order_id"

  attribute {
 name = "order_id"
 type = "S"
  }

  # ✅ PITR 有効化 — 35日間の任意時点に復元可能
  point_in_time_recovery {
 enabled = true
  }
}

解説: RDS の backup_retention_period を 35 日に設定すると、35 日以内の任意の秒単位に PITR (Point-In-Time Recovery) できる。DynamoDB は point_in_time_recovery { enabled = true } を明示しなければデフォルト無効なので注意。誤 DML や誤削除の際に PITR がなければ復元手段がなくなる。本番テーブルでは必ず有効化する。


演習5: 壊れた DynamoDB Streams Lambda — Batch size 100 で重複処理

Before (アンチパターン):

# ❌ Batch size 100 / bisect_on_error なし / report_batch_item_failures なし
resource "aws_lambda_event_source_mapping" "ddb_stream" {
  event_source_arn  = aws_dynamodb_table.orders.stream_arn
  function_name  = aws_lambda_function.stream_processor.arn
  starting_position = "LATEST"
  batch_size  = 100  # ❌ 1件失敗で100件全リトライ
  # bisect_on_batch_function_error なし → 毒薬メッセージでスタック
}
# ❌ 冪等性なし / エラー時に全件リトライ
def handler(event, context):
 for record in event['Records']:
  if record['eventName'] == 'INSERT':
new_image = record['dynamodb']['NewImage']
# 重複チェックなし → Lambda が同じ record を複数回処理する可能性
process_order(new_image)

After (正解パターン):

# ✅ Batch size 10 / bisect_on_error / report_batch_item_failures
resource "aws_lambda_event_source_mapping" "ddb_stream" {
  event_source_arn  = aws_dynamodb_table.orders.stream_arn
  function_name  = aws_lambda_function.stream_processor.arn
  starting_position = "LATEST"
  batch_size  = 10# ✅ 失敗影響範囲を最小化

  # ✅ 失敗時に Batch を2分割して問題レコードを特定
  bisect_batch_on_function_error = true

  # ✅ 部分成功を報告 — 成功した item はリトライしない
  function_response_types = ["ReportBatchItemFailures"]

  destination_config {
 on_failure {
destination_arn = aws_sqs_queue.ddb_dlq.arn
 }
  }
}
# ✅ 冪等性 + ReportBatchItemFailures 対応
import boto3
import json

dynamodb = boto3.resource('dynamodb')
processed_table = dynamodb.Table('processed-events')

def handler(event, context):
 batch_item_failures = []

 for record in event['Records']:
  seq_no = record['dynamodb']['SequenceNumber']
  try:
if record['eventName'] == 'INSERT':
 # 冪等性チェック: 処理済みかどうか確認
 resp = processed_table.get_item(Key={'seq_no': seq_no})
 if 'Item' in resp:
  continue  # 処理済み → スキップ

 new_image = record['dynamodb']['NewImage']
 process_order(new_image)

 # 処理済みとしてマーク
 processed_table.put_item(Item={
  'seq_no': seq_no,
  'ttl': int(__import__('time').time()) + 86400
 })

  except Exception as e:
print(f"Failed to process record {seq_no}: {e}")
batch_item_failures.append(
 {"itemIdentifier": seq_no}
)

 return {"batchItemFailures": batch_item_failures}

def process_order(new_image):
 order_id = new_image['order_id']['S']
 # 注文処理ロジック
 print(f"Processing order: {order_id}")

解説: DynamoDB Streams + Lambda の組み合わせで、Batch size が大きすぎると 1 件の処理失敗で Batch 全体がリトライされ、同じレコードを大量に重複処理する。bisect_batch_on_function_error = true で失敗 Batch を 2 分割して問題レコードを特定し、ReportBatchItemFailures で成功した item はリトライから除外する。Lambda 側では SequenceNumber を使った冪等性チェックを実装して重複処理を防ぐ。


8. まとめ + Vol2予告 + 落とし穴10選 + 全10軸クロスリンク + 第11軸完結宣言

Vol1 振り返り — 3本柱 × Workload 別選定マトリクス

本記事で習得した Database 本番運用 3 本柱の全体像を整理する。

3本柱代表サービス得意 Workload本番必須設定
RDBRDS PostgreSQL/MySQLトランザクション・結合・複雑クエリMulti-AZ + Read Replica + RDS Proxy
分散 RDBAurora MySQL/PostgreSQL高スループット + 自動復旧 + Global DBBacktrack + Auto Scaling Reader + Global Cluster
NoSQLDynamoDB大規模 KV + イベント駆動 + サーバーレスPITR + On-Demand or Auto Scaling + Streams

全10軸との統合視点:
IAM × Database: RDS IAM Database Authentication / DynamoDB Fine-grained access control
EKS × Database: RDS Proxy 経由でコンテナからの接続数を制御
復旧 × Database: PITR + Cross-Region Snapshot Copy + Aurora Global DB Failover
コスト × Database: Reserved Instances (RDS) / DynamoDB On-Demand vs Provisioned 選定
マルチアカウント × Database: AWS RAM での Subnet 共有 / Cross-Account Snapshot
Observability × Database: Performance Insights + CloudWatch DB Metrics + X-Ray
Network × Database: VPC Endpoint for DynamoDB / RDS Subnet Group 設計
DevOps × Database: Schema Migration を Pipeline に組み込む / DynamoDB Seeder


落とし穴10選

  1. RDS Multi-AZ 設定漏れmulti_az = false のまま本番稼働。AZ 障害で手動復旧が必要になる
  2. Read Replica 遅延無視 — 非同期レプリケーションの ReplicaLag を監視せず古いデータを参照
  3. Lambda からの直接 RDS 接続 — 同時実行数増加で max_connections 枯渇。RDS Proxy 必須
  4. Aurora Serverless v2 min ACU 0.5 — コールドスタートなしとはいえ、低 ACU 時の初回クエリは遅延する。本番は min ACU 2 以上を推奨
  5. Aurora Global DB Failover 後の旧 Region 書き込み — Primary 昇格直後に旧 Primary にアプリが書き込む事故。DNS フェイルオーバーと接続先切替を同期させる
  6. DynamoDB Sequential Key で Hot Partitioncreated_at や連番 ID を PK に使うと Throttle が多発する。UUID v4 を使う
  7. GSI Projection ALL の乱用 — 全属性 Projection は書き込みコストが Item サイズ × GSI 数倍になる。必要な属性のみ INCLUDE する
  8. DynamoDB Streams Lambda の重複処理 — Batch size が大きいまま冪等性なしで運用すると、リトライ時に重複処理が発生する
  9. RDS Maintenance Window 未設定auto_minor_version_upgrade = true のまま Maintenance Window を設定しないと業務時間帯に自動パッチが当たる可能性がある
  10. Cross-Region Snapshot Copy で KMS Key 未指定 — 暗号化済みスナップショットを別リージョンにコピーする際、宛先リージョンの CMK を明示しないとコピーが失敗する

本番 Go-Live チェックリスト

Database 本番運用を開始する前に以下を必ず確認する:

RDS/Aurora 共通
– [ ] Multi-AZ が有効になっている (multi_az = true or Aurora Multi-AZ クラスタ)
– [ ] backup_retention_period が 14 日以上に設定されている
– [ ] deletion_protection = true で誤削除を防止している
– [ ] storage_encrypted = true で保存時暗号化が有効になっている
– [ ] Maintenance Window が業務時間外 (深夜帯) に設定されている
– [ ] auto_minor_version_upgrade = false で計画外パッチを防止している
– [ ] Lambda から接続する場合は RDS Proxy を経由している

Aurora 固有
– [ ] Aurora MySQL は backtrack_window = 86400 (24時間) 以上で有効化している
– [ ] Reader Auto Scaling が設定されている (aws_appautoscaling_policy)
– [ ] Aurora Global Database は AuroraGlobalDBReplicationLag をモニタリングしている

DynamoDB
– [ ] point_in_time_recovery { enabled = true } が全テーブルで有効になっている
– [ ] Partition Key が UUID v4 など高カーディナリティな設計になっている
– [ ] GSI の projection_typeALL のままになっていないか確認した
– [ ] DynamoDB Streams + Lambda は bisect_batch_on_function_error = true が設定されている
– [ ] Lambda の Stream 処理に冪等性チェックが実装されている


Vol2 予告 — ElastiCache × Redis × Memcached × DAX

Vol2 では Database 本番運用の第2層として キャッシュ層 を扱う。RDS/Aurora/DynamoDB の読み込み負荷を劇的に削減し、レイテンシを数ms → 数μs に引き下げる ElastiCache (Redis Cluster Mode / Memcached) と DynamoDB 専用キャッシュの DAX を、本番品質の IaC で解説する。

Vol2 で扱う技術主なユースケース
ElastiCache Redisセッション / レートリミット / リーダーボード / Pub-Sub
ElastiCache Memcachedシンプル KV キャッシュ / オートスケール
DAXDynamoDB 読み込み高速化 (μ秒レイテンシ)
Redis Cluster Mode大規模シャーディング / 書き込みスケール

全10軸 (22記事) + Database Vol1 — 全23記事クロスリンク


第11軸 Database本番運用 シリーズ起点 完結宣言

本記事により、AWS本番運用 第11軸 = Database本番運用 シリーズが起点 Vol1 から始動しました。

全10軸 (IAM/EKS/復旧/AI/セキュリティ/コスト/マルチアカウント/Observability/Network/DevOps) の 22 記事で構築した本番運用基盤の上に、RDS × Aurora × DynamoDB の 永続データ層 を加えることで、AWS 本番システムの全レイヤーが揃います。

次回 Vol2 では キャッシュ層 (ElastiCache × DAX) を加え、アプリケーション全体のレイテンシとコスト最適化を完成させます。