RDS・Aurora 運用・トラブル解決ガイド|仕組み・障害切り分け・性能監視の全体像

目次
  1. 🗺️ RDS・Aurora の運用で何が起きるのか:トラブルの全体像をつかむ
  2. 🏗️ 仕組みから理解する Aurora のアーキテクチャ要点
  3. 🔌 接続まわりのトラブル:つながらない・切れる・詰まる
  4. 🐢 性能劣化のトラブル:遅いのはどこか
  5. 💾 ストレージと内部構造に起因するトラブル
  6. 📊 監視とメトリクス設計:異常に気づける状態を作る
  7. 🔄 バックアップ・リストア・バージョンアップを安全に回す
  8. ⚖️ 構成とコストの見直しで障害の芽を減らす
  9. ❓ よくある質問
  10. 📚 学習の進め方:この先どの記事から読むか
本記事にはプロモーション(広告)が含まれています。

Amazon RDS と Aurora は「マネージドだから運用が楽」と言われます。実際、OSパッチやレプリケーション構築といった作業は消えました。ただ、消えたのは作業であって、データベースそのものの仕組みは消えていません。フェイルオーバーで接続が切れ続ける、レプリカを増やしても遅い、ストレージが減らない。こうした事象の原因は、たいていマネージドサービスの外側(アプリの接続の持ち方)か、エンジン内部(MVCC、ロック、実行計画、バッファ)のどちらかにあります。

この記事は、個別の症状ごとに検索して断片的な対処法を拾う前に、「いま自分が見ている現象は、どの層の、どの仕組みに起因するのか」を判断できるようにするための入口です。Aurora のストレージ分離アーキテクチャから、接続・性能・肥大化のトラブル分類、監視設計、バージョンアップ、構成見直しまでを一枚の地図として整理します。

🗺️ RDS・Aurora の運用で何が起きるのか:トラブルの全体像をつかむ

RDS(通常) と Aurora は「どこが違うか」で障害の出方が変わる

RDS(通常、いわゆる RDS for PostgreSQL / MySQL など)は、EC2 相当のインスタンスに EBS ボリュームを付け、その上でコミュニティ版のエンジンをほぼそのまま動かす構成です。マルチAZ構成では、スタンバイへ同期的にブロックレベルのレプリケーションを行います。「1インスタンス+1ボリューム」が基本単位なので、ストレージI/Oの性能特性は EBS のボリュームタイプ(gp2/gp3/io1/io2)とプロビジョンド IOPS に強く縛られます。

Aurora は、ストレージ層をクラスター全体で共有する専用の分散ストレージに置き換えています。インスタンスは計算とバッファプールを持つだけで、データの実体はクラスターストレージにあります。この違いが、障害の出方を大きく変えます。

RDS(通常)では「ストレージ枯渇」「EBSバーストクレジット枯渇」「レプリケーションスレッドの詰まりによるレプリカ遅延」が起きます。Aurora ではストレージが自動拡張されるので枯渇しにくい代わりに、ローカルストレージ(一時領域)の枯渇、I/O課金の増大、クォーラム越しの書き込みレイテンシという別種の問題が出ます。

フェイルオーバーの挙動も違います。RDS マルチAZはスタンバイへの切り替えに分単位かかることがあり、Aurora はリーダーを昇格させるだけなので一般に短時間で済みます(ただしゼロではなく、切り替え後のキャッシュ状態の問題が残ります)。自分の環境がどちらなのかで、疑うべき原因の優先順位が変わります。

アプリ・接続・SQL・ストレージ・基盤——5つの層で切り分ける

「DBが遅い」という報告を受けたとき、闇雲にメトリクスを眺めても進みません。次の5層のどこで時間を消費しているかを順に潰します。

  1. アプリ層。N+1クエリ、リトライの暴走、バッチとオンラインの同時実行。DB側のメトリクスが平穏ならここです。
  2. 接続層。コネクションプールの枯渇、DNSキャッシュ、TCPのタイムアウト設定、max_connections 到達。「特定の時間帯だけ一斉にエラー」はここを疑います。
  3. SQL層。実行計画の変化、統計情報の陳腐化、ロック競合。「特定のクエリだけ遅い」「ある日から突然遅い」はここ。
  4. ストレージ/内部構造層。テーブル肥大化、インデックス膨張、autovacuum の遅れ、一時領域枯渇。じわじわ悪化するタイプ。
  5. 基盤層。インスタンスクラスのCPU/メモリ不足、ネットワーク帯域、AWS側のイベント。

切り分けの原則は「上から下へ、広いものから狭いものへ」です。まず CloudWatch で DB 全体の負荷が上がっているかを見て、上がっていなければアプリ・接続層、上がっていれば Performance Insights で「何の待機イベントで時間を使っているか」に降りていきます。

最初に見るべき情報源:CloudWatch、Performance Insights、DBログ、イベント

役割はきれいに分かれています。

CloudWatch メトリクスは、インスタンス単位の外形的な指標です。CPU、接続数、ストレージ、レプリカラグ。「いつ何が起きたか」の時刻特定に使います。Performance Insights は DBセッションの内訳で、「その時刻に、どのSQLが、どの待機イベントで待っていたか」を出します。原因特定の主役です。DBログ(PostgreSQL ログ/MySQL エラーログ・スローログ)には、エラーメッセージ、デッドロック検出、チェックポイント警告、autovacuum の実行記録など、メトリクスに出ない事実が残ります。RDS イベントは、フェイルオーバー、再起動、ストレージ自動拡張、メンテナンス適用といった AWS 側の操作履歴です。

💡 インシデント対応では、この4つを同じ時間軸に並べるのが基本動作です。イベントに「failover」が記録されていれば、アプリ側の大量エラーの説明はそれで付きます。

# 直近24時間のイベントを確認(クラスター/インスタンス単位で指定)
aws rds describe-events \
  --source-identifier my-aurora-cluster \
  --source-type db-cluster \
  --duration 1440

🏗️ 仕組みから理解する Aurora のアーキテクチャ要点

コンピュートとストレージの分離:REDOログだけを転送する書き込みモデル

通常の PostgreSQL / MySQL では、コミット時に WAL(REDOログ)をディスクへ同期書き込みし、さらにバックグラウンドでダーティページをデータファイルへ書き出します。チェックポイント時にはまとまった量のページ書き込みが発生し、I/O のスパイクになります。マルチAZ構成ではこのページ書き込みも含めてスタンバイ側へ転送されます。

Aurora はここを変えています。ライターインスタンスがストレージ層へ送るのは基本的に REDOログレコードだけで、データページの構築(ログの適用)はストレージノード側が担当します。ページ全体を書かないため、ネットワークに流れる量が小さく、チェックポイントによる書き込みスパイクという概念も通常のエンジンほど前面に出ません。

💡 この設計を理解していると、次の判断ができます。Aurora で書き込みが遅いとき、疑うべきはページI/Oではなくコミット時のログ永続化の往復(PostgreSQL なら IO:XactSync、Aurora MySQL なら redo log flush 系の待機)です。大量の小さなトランザクションをループで投げる実装は Aurora では特に不利で、まとめてコミットする、バルク処理に変えるといった改善が効きます。

3AZ・6コピーのストレージと、クォーラムによる読み書きの成立条件

Aurora のクラスターストレージは、データを 10GB 単位のセグメントに分割し、3つのアベイラビリティゾーンに合計6コピー(各AZ 2コピー)保持します。書き込みは 6 のうち 4 の応答で成立、読み取りは 3 のコピーで成立する、というクォーラム方式です。

この構成が意味するのは、「AZ 1つ丸ごと+別AZの1コピー」が失われても書き込みを継続でき、「AZ 1つ丸ごと」が失われても読み取りは継続できる、という耐障害性です。運用上の含意は2つあります。ストレージ層はインスタンス障害から独立しているため、ライターが落ちてもデータは失われず、リーダーを昇格させるだけで復旧できます。これがフェイルオーバーが速い理由です。一方で書き込みレイテンシはクォーラム成立までの時間に依存します。AZ間の通信が絡むため、単一ノードのローカルディスクより理論的な下限は大きくなります。「1件ずつコミット」が不利なのはこのためです。

ライター/リーダーとレプリカラグが生まれる理由

Aurora のリーダーインスタンスは、同じクラスターストレージを参照します。MySQL のバイナリログレプリケーションや PostgreSQL のストリーミングレプリケーションのように「データを転送して再生する」構造ではありません。

ではなぜレプリカラグが存在するのか。リーダーは自分のバッファプールにキャッシュしたページを持っており、ライター側の更新を反映させるために REDO ログレコードの通知を受けてキャッシュを無効化・更新する必要があるからです。この伝搬にかかる時間が AuroraReplicaLag として現れます。通常は非常に小さい値に収まりますが、

  • ライター側の更新量が急増したとき
  • リーダー側で長時間のクエリが走り、MVCCの可視性維持のために処理が滞るとき

にラグが伸びます。ラグが伸びた状態で「書いた直後に読む」処理をリーダーに投げると、古いデータが返る可能性があります。読み取り一貫性が必要な処理はライターへ振る、という設計が必要になるのはこの性質のためです。

バッファプール・共有メモリとフェイルオーバー後のウォームアップ

Aurora でフェイルオーバーが数十秒で終わったのに、その後しばらくアプリが遅い。これはよくある現象で、原因はバッファプールです😇

昇格したリーダーは、それまでリーダーとして使われていた分のキャッシュは持っていますが、ライターが扱っていたワークロード(特に書き込み系のホットなページ)はキャッシュに乗っていません。結果として、フェイルオーバー直後はストレージからのページ読み取りが増え、レイテンシが上がります。Aurora PostgreSQL では待機イベント IO:DataFileRead の増加として観測できます。

💡 判断基準としては、フェイルオーバー後 5〜15分で徐々に改善していくならウォームアップ中と見てよいです。恒常的に改善しない場合は別の原因(パラメータ、接続先の偏り、インスタンスクラスの差)を疑います。なお、ライターとリーダーでインスタンスクラスを揃えておかないと、昇格後に性能が足りなくなります。コスト削減のためにリーダーを小さくする構成は、フェイルオーバー時のリスクとセットで判断してください。

🔌 接続まわりのトラブル:つながらない・切れる・詰まる

接続数の上限に達したときの症状と max_connections の考え方

PostgreSQL なら FATAL: sorry, too many clients already、MySQL なら ERROR 1040 (HY000): Too many connections。これが出たときに反射的に max_connections を上げるのは、多くの場合悪手です😇

理由は、PostgreSQL が接続ごとにプロセスを生成するアーキテクチャだからです。接続1本あたりにプロセスのメモリ(work_mem はソートやハッシュごとに確保されるため、1セッションが複数倍使うこともある)が乗ります。接続数を増やせば、アイドル接続が大量にメモリとコンテキストスイッチを消費し、上げた結果として全体が遅くなることが起こり得ます。

RDS/Aurora の max_connections はデフォルトでインスタンスクラスのメモリから算出される式が入っています(PostgreSQL 系なら LEAST({DBInstanceClassMemory/9531392}, 5000) のような形)。まず確認すべきは、現在の接続の内訳です。

-- PostgreSQL: 状態別の接続数を確認
SELECT state, count(*)
FROM pg_stat_activity
GROUP BY state
ORDER BY 2 DESC;

-- idle in transaction が長時間残っていないか
SELECT pid, usename, application_name, state,
       now() - state_change AS duration, left(query, 80)
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDER BY duration DESC;

idle が大量にあるならアプリ側のプール設定過剰、idle in transaction が長時間残っているならアプリのトランザクション制御のバグ(コミット漏れ、例外時のロールバック漏れ)です。後者は後述する autovacuum の阻害要因にもなるため、接続数の問題にとどまりません。対策として idle_in_transaction_session_timeout の設定を検討します。

フェイルオーバー時にアプリがエラーを出し続ける原因(DNSキャッシュとコネクションプール)

Aurora のフェイルオーバーは、クラスターエンドポイントの DNS レコードが新しいライターの IP を指すように書き換わることで完了します。RDS 側の DNS TTL は短く設定されています(Aurora のエンドポイントは 5 秒程度)。にもかかわらずアプリがエラーを出し続けるのは、クライアント側が DNS をキャッシュしているか、切断済みのコネクションをプールが握り続けているかのどちらかです。

典型は Java です。JVM はセキュリティマネージャの設定次第で DNS の正引き結果を無期限にキャッシュします。明示的に短くしておく必要があります。

// JVM 起動時、または初期化コードで
java.security.Security.setProperty("networkaddress.cache.ttl", "5");
java.security.Security.setProperty("networkaddress.cache.negative.ttl", "0");

もう一つがコネクションプールです。フェイルオーバー前に確立された TCP コネクションは、切り替え後には旧ライター(=リーダーに降格)を指しているか、単に切断されています。プールがそれを「生きている」と誤認して払い出すと、server closed the connection unexpectedly や、PostgreSQL なら cannot execute INSERT in a read-only transaction といったエラーになります。後者は「接続先がリーダーになっているのに書き込もうとした」ことを示す明確なサインです😇

対策の方向は3つです。

  1. プールに接続の生存確認を入れる(HikariCP なら connectionTestQuery や validationTimeout、maxLifetime を短めに)。
  2. アプリ側にリトライを実装する。フェイルオーバーは数十秒で終わるので、指数バックオフ付きのリトライが有効。
  3. RDS Proxy や、エンジン純正のフェイルオーバー対応ドライバ(AWS Advanced JDBC Wrapper など)を使い、切り替え検知をクライアント側から外す。

クラスターエンドポイント・リーダーエンドポイント・カスタムエンドポイントの使い分け

Aurora には複数のエンドポイントがあり、役割を混同すると事故になります。

クラスターエンドポイント(ライターエンドポイント)は常に現在のライターを指し、書き込みは必ずここへ向けます。リーダーエンドポイントはリーダーインスタンス群へ DNS ラウンドロビンで振り分けますが、注意すべきは接続時に1回解決されるだけで、クエリ単位の負荷分散ではない点です。接続を長く持ち続けるアプリでは、特定リーダーに接続が偏ることがあります。インスタンスエンドポイントは特定インスタンスを直接指すもので、メンテナンスや調査用です。アプリから常用すると、フェイルオーバーで昇格した際に追随できません。カスタムエンドポイントは任意のインスタンス群をまとめたもので、「分析バッチ用の大きいリーダー」と「オンライン参照用のリーダー」を分ける、といった使い方をします。

リーダーの偏りが疑われる場合は、各インスタンスの DatabaseConnections を比較します。偏っているなら、プールの maxLifetime を設定して定期的に接続を張り直させるか、カスタムエンドポイントで明示的に分離します。

RDS Proxy を入れる判断基準と、入れても解決しないケース

RDS Proxy は、アプリとDBの間でコネクションをプール・多重化するマネージドサービスです。入れる価値が高いのは、Lambda など接続を持続できない実行環境から大量に接続するケース、接続の確立・切断が高頻度で DB側のプロセス生成コストが無視できないケース、フェイルオーバー時のエラー時間を短くしたいケース(Proxy が切り替えを吸収する)です。

一方で、入れても解決しない・かえって悪化するケースもあります。

遅いクエリが原因の詰まりには効きません。Proxy は接続を束ねるだけで、SQL は速くなりません。むしろ待ち行列が Proxy 側に移動して見えにくくなります。長時間トランザクションが多い環境では、Proxy の接続ピン留め(pinning)が発生して多重化の効果が失われます。一時テーブルの利用、プリペアドステートメント、セッション変数の設定などがピン留めの原因になります。CloudWatch の DatabaseConnectionsCurrentlySessionPinned で確認できます。また、1ホップ増える分のオーバーヘッドがあるため、わずかなレイテンシ増が許容できないワークロードには向きません😇

🐢 性能劣化のトラブル:遅いのはどこか

待機イベントから読む「CPUで詰まっているのか、I/Oか、ロックか」

性能分析の中心は Performance Insights の「DBロード(平均アクティブセッション数, AAS)」です。これが vCPU 数のラインを超えているかどうかが最初の判断基準になります。超えていれば、DBはリクエストを捌ききれていません。

次に見るのは、その負荷の内訳=待機イベントです。PostgreSQL 系での代表的な読み方は次のとおりです。

待機イベント 意味 主な対処方向
CPU 実際に計算している 実行計画の非効率、インデックス不足、インスタンスクラス
IO:DataFileRead ストレージからのページ読み取り バッファプール不足、全表スキャン、キャッシュのコールドスタート
IO:XactSync コミットのログ永続化待ち コミット頻度が高すぎる、バッチ化の検討
Lock:* 行ロック・テーブルロック待ち ロック競合、長時間トランザクション
LWLock:* 内部の軽量ロック競合 バッファ競合、WAL書き込み競合、接続過多
Client:ClientRead アプリからの次の入力待ち DBではなくアプリ/ネットワーク側の問題

💡 Client:ClientRead が上位に来ている場合、DBは暇であり、原因はアプリ側にあります。この判別ができるだけで、調査の方向を大きく間違えずに済みます。

Aurora MySQL では io/aurora_redo_log_flush(コミット待ち)、synch/mutex/innodb/*(内部競合)、io/table/sql/handler(テーブル読み取り)といったイベント名になります。名前は違っても「CPU/I/O/ロック/クライアント待ち」の4分類で読む考え方は同じです。

実行計画が突然変わる:統計情報・パラメータ・データ量の影響

「昨日まで速かったクエリが今日から遅い」。デプロイもデータ変更もしていないのに起きる場合、オプティマイザが選ぶ実行計画が変わったことを疑います。原因は主に次の3つです😇

  1. 統計情報の更新。ANALYZE(PostgreSQL)や autovacuum によるサンプリング更新で、行数やカーディナリティの推定が変わり、インデックススキャンからシーケンシャルスキャンへ切り替わる、といった変化が起きます。
  2. データ量の閾値越え。テーブルが成長し、インデックスを使うコストと全件読むコストが逆転した。あるいはインデックスの相関(correlation)が悪化してランダムI/Oのコスト見積もりが上がった。
  3. パラメータ変更。work_mem、random_page_cost、effective_cache_size、jit の有効化など。Aurora のようにストレージがネットワーク越しでもキャッシュが効く環境では、random_page_cost のデフォルト値が実態と合わないことがあります。

確認は実際の実行計画を取るのが最短です。推定値だけの EXPLAIN ではなく、実測を伴う形で取ります。

-- PostgreSQL: 実測付きの実行計画。バッファヒット率も見る
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT ...;

-- 統計情報の最終更新時刻と推定行数
SELECT relname, n_live_tup, n_dead_tup,
       last_analyze, last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;

EXPLAIN ANALYZE で推定行数(rows)と実測行数(actual rows)が桁違いなら、統計情報かクエリ構造(相関のある条件、関数インデックスの欠如)の問題です。PostgreSQL 系なら pg_stat_statements を有効にして、実行回数と総時間の両面から犯人を絞ります。

-- 総実行時間の大きいクエリ上位
SELECT calls, round(total_exec_time) AS total_ms,
       round(mean_exec_time) AS mean_ms, rows,
       left(query, 100) AS query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 20;

1回あたりは速くても呼び出し回数が膨大なクエリ(N+1)は、mean_exec_time だけ見ていると見落とします。total_exec_time 順で見るのが鉄則です。

ロック競合とブロッキングセッションの特定、長時間トランザクションの扱い

Lock:transactionid や Lock:tuple が待機イベントの上位に出たら、誰が誰を待たせているかを直接調べます。

-- PostgreSQL: ブロックしている側とされている側を対で出す
SELECT blocked.pid   AS blocked_pid,
       blocked.query AS blocked_query,
       blocking.pid  AS blocking_pid,
       blocking.query AS blocking_query,
       now() - blocking.xact_start AS blocking_xact_age
FROM pg_stat_activity blocked
JOIN LATERAL unnest(pg_blocking_pids(blocked.pid)) AS b(pid) ON true
JOIN pg_stat_activity blocking ON blocking.pid = b.pid
WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0;

実務上、待たせている側は「まだ実行中の重いUPDATE」よりも「アプリがコミットを忘れて idle in transaction のまま放置している接続」であることが少なくありません。この場合、SQL チューニングではなくアプリのトランザクション境界の設計が問題です。

⚠️ DDL は特に注意が必要です。PostgreSQL の ALTER TABLE は ACCESS EXCLUSIVE ロックを取り、そのロック待ちがキューに入ると、後から来た SELECT まで芋づる式にブロックされます。本番でのDDLは lock_timeout を短く設定して、取れなければ諦めて再試行する運用が安全です。

SET lock_timeout = '3s';
ALTER TABLE orders ADD COLUMN memo text;

リードレプリカに振っても速くならないときに疑うこと

参照をリーダーへ逃がしたのに改善しない場合、次のいずれかが起きています。

  • 書き込みがボトルネックになっている。そもそも遅いのが UPDATE/INSERT なら、リーダーを増やしても無意味です。Aurora はライターが1台という制約があるため、書き込みスケールはインスタンスクラスの上限に縛られます(シャーディングやアーキテクチャ変更の検討領域)。
  • 接続がリーダーに分散していない。前述のとおり、リーダーエンドポイントは接続時のDNS解決でしか分散しません。
  • 同じ重いクエリを各リーダーで実行しているだけ。1クエリあたりの実行時間は変わりません。スループットは上がってもレイテンシは改善しません。
  • リーダー側で長時間クエリが走り、ラグが拡大している。結果として参照結果の鮮度が落ち、アプリ側でリトライが発生します。
  • ボトルネックがDBの外にある。Client:ClientRead が支配的なら、アプリのデシリアライズやネットワークが原因です。

💡 「スケールアウトで解決するのは、同時実行数が足りない問題だけ」という原則を持っておくと判断を誤りません。

💾 ストレージと内部構造に起因するトラブル

PostgreSQL の MVCC とテーブル肥大化:autovacuum が追いつかない状態

PostgreSQL は MVCC を「行の追記」で実現します。UPDATE は既存行を書き換えるのではなく、新しいバージョンの行(タプル)を追加し、古い行を「不要になった候補」として残します。DELETE も同様に、削除マークを付けるだけです。この不要タプル(デッドタプル)を回収するのが VACUUM であり、通常は autovacuum が自動実行します。

autovacuum が追いつかないと、テーブルとインデックスが物理的に膨らみ続けます。結果として、

  • 同じ行数でもスキャンするページ数が増え、I/O が増える
  • バッファプールに無駄なページが乗り、キャッシュヒット率が落ちる
  • インデックスも膨張し、探索が遅くなる

という形で、じわじわと性能が劣化します。「特に何も変えていないのに数か月かけて遅くなった」典型パターンです😇

追いついていないかどうかは、デッドタプル率と最終実行時刻で判断します。

SELECT relname,
       n_live_tup, n_dead_tup,
       round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS dead_pct,
       last_autovacuum, autovacuum_count
FROM pg_stat_user_tables
WHERE n_dead_tup > 10000
ORDER BY dead_pct DESC;

autovacuum が動かない原因として頻出するのは次の3つです。

  1. 長時間トランザクションが古いスナップショットを保持している。まだ参照される可能性がある行は削除できません。idle in transaction やレプリカ側の長時間クエリ(hot_standby_feedback 有効時)が犯人になります。
  2. しきい値の設計が大きなテーブルに合っていない。デフォルトの autovacuum_vacuum_scale_factor = 0.2 は「行数の20%がデッドになったら実行」の意味です。1億行のテーブルでは2000万行溜まるまで動きません。大きなテーブルには個別にテーブル単位のストレージパラメータを設定します。
  3. コスト制限で速度が抑えられている。autovacuum_vacuum_cost_limit と autovacuum_max_workers が小さいと、更新量に処理が追いつきません。
-- 大テーブルだけ閾値を厳しくする例
ALTER TABLE large_events SET (
  autovacuum_vacuum_scale_factor = 0.01,
  autovacuum_vacuum_threshold = 5000,
  autovacuum_analyze_scale_factor = 0.005
);

なお、Aurora MySQL / RDS MySQL(InnoDB)では UNDO ログとパージスレッドが同じ役割を担います。長時間トランザクションが UNDO のパージを阻害し、履歴リスト長(History List Length)が膨らむ、という形で同種の問題が起きます。長いトランザクションが内部の回収処理を止めるという構図は、エンジンを問わず共通です。

トランザクションID周回の警告が出たときの緊急対応と予防

⚠️ PostgreSQL のトランザクションID(XID)は32bitで循環します。VACUUM による「凍結(freeze)」が行われないまま XID を消費し続けると、古いデータが未来のものとして見えてしまうため、PostgreSQL は安全装置として残り約100万トランザクションの時点で新規書き込みを停止します。

RDS では、残り XID が閾値を下回るとイベント通知が出ます。CloudWatch の MaximumUsedTransactionIDs を監視対象に入れておくのが正攻法です。現状の確認はこうします。

-- データベース単位で、凍結までの余裕を確認
SELECT datname,
       age(datfrozenxid) AS xid_age,
       2147483647 - age(datfrozenxid) AS remaining
FROM pg_database
ORDER BY xid_age DESC;

-- どのテーブルが古いXIDを抱えているか
SELECT relname, age(relfrozenxid) AS xid_age, pg_size_pretty(pg_total_relation_size(oid))
FROM pg_class
WHERE relkind IN ('r','m')
ORDER BY age(relfrozenxid) DESC
LIMIT 20;

警告が出た段階での対応は、原因となっているテーブルに対して明示的に VACUUM (FREEZE) を実行することです。ただし大きなテーブルでは長時間かかるため、並列度やコスト遅延の一時的な緩和(vacuum_cost_delay を 0 に近づける)を検討します。同時に、阻害要因となっている長時間トランザクションやレプリケーションスロットを先に解消しないと、VACUUM を流しても凍結が進みません。放置されたレプリケーションスロット(論理レプリケーションの残骸など)は pg_replication_slots で確認します。

予防策は明快で、(1) 長時間トランザクションを作らない、(2) 大きなテーブルの autovacuum しきい値を調整する、(3) MaximumUsedTransactionIDs にアラートを張る、の3点です。

一時領域・ローカルストレージ枯渇(ソート、一時テーブル、大きなクエリ)

Aurora ではデータ本体がクラスターストレージにあり自動拡張されますが、インスタンスのローカルストレージは固定容量です。ここを使うのは、

  • work_mem に収まらないソートやハッシュ結合の一時ファイル
  • 一時テーブル
  • エンジンが内部で使う作業領域

です。巨大な ORDER BY や GROUP BY、結合順序を誤った実行計画によって一時ファイルが膨れ、FreeLocalStorage がゼロに近づくと、クエリのエラーやインスタンスの再起動につながります。

監視すべきは CloudWatch の FreeLocalStorage(Aurora)または FreeStorageSpace(RDS通常)です。ローカルストレージの容量はインスタンスクラスに比例するため、「大きなクエリを流すからインスタンスクラスを上げる」という判断は、メモリだけでなく一時領域の観点からも妥当です。

PostgreSQL では一時ファイルの発生をログに出して原因クエリを特定できます。

-- パラメータグループで設定(単位はkB。0で全件記録、-1で無効)
-- log_temp_files = 10240   -- 10MB を超える一時ファイルをログ出力

根本対策は、work_mem をセッション単位で引き上げる(全体に上げると接続数×並列度で膨れるため危険)、インデックス追加でソートを回避する、バッチを分割する、といった SQL 側の改善です。エンジンとバージョンによっては一時領域をクラスターストレージ側へ逃がすオプションが用意されている場合があるため、利用中のバージョンのドキュメントを確認してください。

ストレージ使用量が減らない・想定より増えるときの調べ方

「大量に DELETE したのにストレージ使用量が減らない」。これは MVCC の帰結であり、正常な挙動です。削除された行はデッドタプルになりますが、VACUUM は基本的にファイル内に空き領域を作るだけで、OS へ容量を返しません(テーブル末尾の連続した空き領域のみ切り詰められます)😇

物理的に縮めたい場合の選択肢は、

  • VACUUM FULL:排他ロックを取り、テーブルを作り直す。ダウンタイムを伴うため本番では慎重に。
  • pg_repack 拡張:オンラインで再編成できる。RDS/Aurora でサポートされているか、バージョンと拡張の対応表を確認する。
  • テーブルの再作成+入れ替え:もっとも制御しやすいが手間がかかる。

まず「どこが太っているのか」を測ります。

-- 上位のテーブル/インデックスサイズ
SELECT relname,
       pg_size_pretty(pg_total_relation_size(oid)) AS total,
       pg_size_pretty(pg_relation_size(oid))       AS table_only,
       pg_size_pretty(pg_indexes_size(oid))        AS indexes
FROM pg_class
WHERE relkind = 'r'
ORDER BY pg_total_relation_size(oid) DESC
LIMIT 20;

インデックスだけが不釣り合いに大きい場合は、インデックス膨張を疑い REINDEX CONCURRENTLY(PostgreSQL 12 以降)を検討します。

なお、Aurora のクラスターストレージ課金は、実際に使用している量に対して行われ、データ削除後に確保済み容量が縮小される仕組みが入っています(挙動はバージョンにより異なるため、VolumeBytesUsed メトリクスの推移で実測確認するのが確実です)。RDS(通常)の EBS ストレージは拡張のみ可能で縮小できないため、見積もりミスの影響が長く残ります。この非対称性は構成設計時に意識しておく点です。

📊 監視とメトリクス設計:異常に気づける状態を作る

最低限監視すべき CloudWatch メトリクスと、その値が何を意味するか

すべてのメトリクスにアラートを張ると運用が破綻します。まずは「異常の入口になる」ものに絞ります。

  • CPUUtilization。高いこと自体は悪ではなく、使い切っているだけとも言えます。問題は、高止まりしていて待ち行列ができている状態。DBロードと併せて見ます。
  • DatabaseConnections。max_connections に対する比率で見ます。急増は接続リークかトラフィック増。
  • FreeableMemory。極端に減っていれば、バッファプールや接続あたりのメモリ設定を疑います。スワップ発生(SwapUsage)は明確な警告サイン。
  • ReadIOPS / WriteIOPS / ReadLatency / WriteLatency(RDS通常)。EBS の上限やバーストクレジットに張り付いていないか。
  • FreeStorageSpace(RDS通常)/ FreeLocalStorage(Aurora)。枯渇はサービス停止に直結します。
  • AuroraReplicaLag。参照振り分けの整合性に直結します。
  • DMLThroughput / Queries / Deadlocks。ワークロードの変化検知。
  • MaximumUsedTransactionIDs(PostgreSQL)。XID周回の予兆。

CPU や接続数のような「症状」だけでなく、MaximumUsedTransactionIDs や FreeLocalStorage のような「静かに進行して突然落ちる」系のメトリクスを必ず含めてください。

Performance Insights と拡張モニタリングの役割分担

混同されがちですが、見ている対象が違います。Performance Insights は DBエンジン内部のセッションと待機イベント、つまり「どのSQLが、何を待って、どれだけ時間を使ったか」を見るもので、SQLチューニングの起点になります。拡張モニタリング(Enhanced Monitoring)は OSレベルのメトリクスで、プロセス単位のCPU・メモリ、ロードアベレージ、ディスクI/O。最短1秒粒度で取得でき、標準の CloudWatch(60秒粒度)では潰れてしまう短いスパイクを捉えられます。

使い分けの目安は、「SQLが悪いのか」を調べるなら Performance Insights、「インスタンスのリソースが本当に足りないのか、瞬間的に何かが起きているのか」を調べるなら拡張モニタリングです。両方を有効にしておくのが望ましいですが、拡張モニタリングは粒度を細かくするほどログ取り込み料金が増えるため、常時1秒粒度にするかは環境の重要度で判断します。

Performance Insights のデータ保持期間は、無料枠と有償で異なります。障害後に「1か月前と比較したい」となったときに保持期間が足りない事態を避けるため、本番環境では保持期間の延長を検討する価値があります(料金体系は公式ドキュメントで確認してください)。

スロークエリログ・監査ログの出し方と、出しすぎによる副作用

PostgreSQL では log_min_duration_statement で、指定ミリ秒を超えたクエリをログに記録します。MySQL では slow_query_log と long_query_time です。

-- PostgreSQL パラメータグループの例
-- log_min_duration_statement = 1000   -- 1秒超のクエリを記録
-- log_lock_waits = on                 -- ロック待ちを記録
-- log_temp_files = 10240              -- 大きい一時ファイルを記録
-- log_autovacuum_min_duration = 0     -- autovacuum の実行を記録

副作用は正しく見積もっておく必要があります。

⚠️ log_min_duration_statement = 0(全クエリ記録)は本番では危険です。高トラフィック環境ではログ書き込み自体がI/Oとディスクを圧迫し、それ自体が性能問題になります。調査目的で一時的に設定し、必ず戻します。監査ログ(PostgreSQL の pgaudit、Aurora MySQL の Advanced Auditing)は、コンプライアンス要件がある場合に必要ですが、記録対象を広く取ると同様にオーバーヘッドが出るため、対象テーブル・操作種別を絞り込みます。CloudWatch Logs へエクスポートする設定にすると、ログの保管・検索は楽になりますが、取り込み量に応じた料金が発生します。

log_lock_waits と log_autovacuum_min_duration は、オーバーヘッドが小さく得られる情報が多いため、本番でも有効にしておく価値が高い設定です。

アラートのしきい値をどう決めるか(平常時のベースラインを取る)

「CPU 80% で警告」といった一般論のしきい値は、多くの環境で機能しません。バッチ処理中は常に90%を超える環境もあれば、50%でレイテンシが悪化する環境もあります。

現実的な手順は次のとおりです。

  1. 平常時(1〜2週間)のメトリクスを観測し、日次・週次の周期性を把握する。月末バッチのような月次の山も忘れずに。
  2. ピーク時の値を基準に、「平常ピークを明確に超えたら警告」というしきい値を置く。
  3. 単一メトリクスではなく、複合条件で判定する。例:「CPU 90%超」だけではなく「CPU 90%超かつ DBロードが vCPU 数を上回る状態が5分継続」。
  4. 枯渇系(ストレージ、XID、接続数)は絶対値で厳格に。これらは超えた瞬間にサービス停止するため、余裕を持った段階で通知する。
  5. 通知を重大度で分ける。即時対応が必要なもの(枯渇、フェイルオーバー、書き込み停止)と、翌営業日に見ればよいもの(デッドタプル率の上昇、レプリカラグの軽微な増加)を混ぜない。

アラートが鳴りすぎて無視される状態(アラート疲れ)は、監視がない状態とほぼ同じです。鳴ったら必ず何か行動する、というレベルまで絞り込むのが監視設計の目標です😇

🔄 バックアップ・リストア・バージョンアップを安全に回す

自動バックアップとPITRで「どこまで戻せるか」を把握する

RDS/Aurora の自動バックアップは、日次のスナップショットとトランザクションログの継続的な保存で構成され、保持期間内の任意の時点へリストアできます(PITR: Point-in-Time Recovery)。保持期間は 0〜35 日の範囲で設定します(0 は自動バックアップ無効)。

運用で必ず確認しておくべきは2点です。ひとつは復元可能な最新時刻。ログの保存には遅延があるため、「今から1分前」に戻せるとは限りません。もうひとつは、復元が新しいインスタンス/クラスターの作成であるという点です。既存のDBを巻き戻すのではなく、別の識別子で新規作成されます。したがって、アプリの接続先切り替え(エンドポイント変更、DNS、パラメータストアの更新)までが復旧手順に含まれます。ここを手順書に書いていないと、本番の復旧時に止まります。

# 復元可能な最新時刻を確認
aws rds describe-db-clusters \
  --db-cluster-identifier my-aurora-cluster \
  --query 'DBClusters[0].LatestRestorableTime'

35日を超える保管が必要なら、手動スナップショットか AWS Backup を併用します。手動スナップショットは明示的に削除するまで残り、削除時の事故を防げる一方、課金対象であることを忘れないようにします。

💡 復旧手順は定期的に実際に実行して所要時間を測ってください。RTO を「たぶん1時間」と見積もっていても、数TBのスナップショット復元後にストレージの初期化(レイジーローディング)で性能が出ない、という要素まで含めると大きくずれます。

スナップショット復元とAuroraクローンの使いどころの違い

Aurora には、通常のスナップショット復元に加えてクローンがあります。両者は用途が異なります。

スナップショット復元は、バックアップから新しいクラスターを作ります。世代(時点)が固定されており、元のクラスターとは独立しています。障害復旧の本筋です。Aurora クローンはコピーオンライト方式で、同一リージョン内に「現時点のデータを参照する別クラスター」を瞬時に作ります。作成時点ではストレージを追加消費せず、変更が入った部分だけ新しいページが作られます。

クローンが向いているのは、本番相当のデータで検証したいケースです。バージョンアップのリハーサル、重いバッチの影響測定、スキーマ変更の所要時間計測、インデックス追加の効果確認など。本番の性能に影響を与えずに、本番と同じデータ分布・統計情報で試せるのが最大の価値です(データ分布が違うテスト環境では、実行計画が本番と一致しないため、検証の意味が薄れます)。

⚠️ 注意点として、クローンは元クラスターとストレージを共有する期間があるため、元のクラスターを削除する際の依存関係や、書き込みが進むにつれてストレージ使用量(=課金)が増えていく点を把握しておきます。検証が終わったら消す、という運用ルールが必要です。

マイナー/メジャーバージョンアップで起きやすい変化とBlue/Greenデプロイ

マイナーバージョンアップは、基本的にバグ修正とセキュリティ修正であり、互換性の破壊は少ない部類です。それでも、オプティマイザの挙動修正が含まれて実行計画が変わることはあり得ます。

メジャーバージョンアップは、より広範な影響を想定すべきです。代表的な変化点は、

  • オプティマイザの改善による実行計画の変化(良くなることが多いが、悪化するケースもある)
  • デフォルトパラメータの変更
  • 削除・非推奨になった関数・構文・拡張
  • 統計情報のリセット。アップグレード直後は統計が無い/古い状態になることがあり、ANALYZE を実行するまで実行計画が不安定になります。PostgreSQL のメジャーアップグレード後は、明示的に ANALYZE を全体に流すことを手順に組み込みます。
  • 拡張(extension)のバージョン整合。pg_stat_statements や PostGIS などは、アップグレード後に ALTER EXTENSION ... UPDATE が必要になることがあります。

これらのリスクを下げる仕組みが Blue/Green デプロイです。現行環境(Blue)と同じ構成のステージング環境(Green)を作り、Green 側を先にアップグレードした上で、Blue から Green へレプリケーションを張って同期を維持します。十分に検証した後、スイッチオーバーで役割を入れ替えます。

利点は、(1) 本番データで事前検証できる、(2) 切り替え時間が短い、(3) 問題があれば切り替えを中止できる、という点です。ただし、スイッチオーバー時には書き込みが一時的に停止すること、論理レプリケーションの制約(主キーのないテーブルの扱いなど)があることは押さえておく必要があります。

メンテナンスウィンドウとパラメータグループ変更の反映タイミング

パラメータグループのパラメータには動的(dynamic)と静的(static)があり、反映タイミングが違います。動的パラメータは変更後ほぼ即座に反映されます(work_mem、log_min_duration_statement など)。静的パラメータはインスタンスの再起動が必要です(max_connections、shared_buffers に相当するもの、shared_preload_libraries など)。変更しただけでは効かず、ステータスが pending-reboot のまま残ります。

「パラメータを変えたのに効かない」というトラブルの大半はこれです。適用状態は必ず確認します😇

aws rds describe-db-parameters \
  --db-parameter-group-name my-pg15-params \
  --query "Parameters[?ParameterName=='max_connections']"
-- 実際に効いている値を確認(PostgreSQL)
SHOW max_connections;
SELECT name, setting, source, pending_restart
FROM pg_settings
WHERE pending_restart = true;

メンテナンスウィンドウは、AWS側の必須メンテナンス(マイナーバージョン適用、OSパッチ)が実行される時間帯です。業務影響の少ない時間に設定するのは当然として、「自動マイナーバージョンアップグレード」を有効にするかどうかは方針判断になります。有効にすればセキュリティ修正が自動適用される一方、実行計画の変化を事前検証できません。本番では無効にして計画的に適用し、検証環境では有効にして先行して挙動を確認する、という組み合わせが現実的です。

⚖️ 構成とコストの見直しで障害の芽を減らす

インスタンスクラスの選び方と、スケールアップで解決する問題・しない問題

インスタンスクラスの選定で見るべきは vCPU だけではありません。メモリ、ネットワーク帯域、ローカルストレージ容量、EBS帯域(RDS通常の場合)がセットで決まります。

スケールアップで解決するのは、

  • バッファプールにデータが収まらず I/O が多発している(メモリ増で改善)
  • CPU が飽和して待ち行列ができている
  • ローカルストレージ(一時領域)が足りない
  • ネットワーク帯域が上限に達している

スケールアップで解決しないのは、

  • ロック競合(同時実行の直列化はCPUを増やしても解けない)
  • 実行計画の誤り(1000倍非効率なクエリは、2倍のCPUでは救えない)
  • 長時間トランザクションによる VACUUM 阻害
  • アプリ側のボトルネック

💡 判断基準はシンプルで、Performance Insights の待機イベントが CPU や IO:DataFileRead に偏っているならスケールアップの効果が期待できる、Lock:* に偏っているなら効果は薄い、ということです。

⚠️ また、バースト可能な T 系インスタンスクラスは、本番の恒常的な負荷には向きません。CPU クレジットを使い切った後のスロットリングが、原因不明の性能劣化として現れます。CPUCreditBalance を監視していないと気づきにくいため、本番では M 系・R 系を選ぶのが無難です。

Aurora Serverless v2 を選ぶ前に確認しておきたいこと

Aurora Serverless v2 は、ACU(Aurora Capacity Unit)単位で容量を自動調整する構成です。負荷変動が大きい、あるいは予測困難なワークロードでコスト効率が良くなる可能性があります。

選定前に確認すべき点は次のとおりです。

最小 ACU の設定がバッファプールサイズを決めます。最小値を小さくしすぎると、アイドル時にキャッシュが縮み、次のアクセス時にコールドスタート相当の遅さが出ます。「安くするために最小を下げたら遅くなった」は典型的な失敗です。スケール速度も見ておく必要があり、急峻なスパイクに対して容量の追従が間に合わないことがあります。秒単位の応答が求められるワークロードでは検証が必要です。コスト特性としては、常時高負荷であればプロビジョンドインスタンスの方が安くなることが多く、「負荷の谷が深く、長い」場合にメリットが出ます。エンジンバージョンや一部機能に制約がある場合もあるため、最新の対応状況は公式ドキュメントで確認してください。

現実的な使いどころは、開発・検証環境、負荷が読めない新規サービスの初期、参照負荷が変動するリーダーインスタンスへの適用といったパターンです。

I/O課金とストレージ構成が運用判断に与える影響

Aurora には課金体系として Aurora Standard と Aurora I/O-Optimized があります。Standard はストレージ容量+I/Oリクエスト数に対する従量課金、I/O-Optimized は I/O 課金が無い代わりにインスタンスとストレージの単価が高い、という構造です。

この違いは、単なるコストの話ではなく運用判断に影響します。Standard では I/O が多いワークロードほど課金が増えるため、非効率なクエリがコストとして可視化されるという側面があります。逆に I/O-Optimized では、I/O が増えてもコストは一定なので予測しやすくなります。

切り替えの目安として、AWS は「総コストに占める I/O コストの割合が一定以上なら I/O-Optimized が有利」という考え方を示しています。CloudWatch の VolumeReadIOPs / VolumeWriteIOPs(クラスター単位)と Cost Explorer の実績から判断します。具体的な閾値や単価は変動するため、公式の料金ページで最新情報を確認してください。

RDS(通常)では、ボリュームタイプの選択が直接的に効きます。gp2 はボリュームサイズに比例したベースライン性能とバーストクレジットの仕組みを持つため、小容量ボリュームで高IOPSを求めるとクレジット枯渇による突然の性能低下が起きます。gp3 は容量とIOPSを独立して設定できるため、この問題を避けやすい構成です。既存の gp2 環境で原因不明のI/O性能劣化が出ている場合は、BurstBalance メトリクスを確認してください。

マルチAZ・リージョン構成をどこまでやるかの判断軸

可用性構成は「やればやるほど良い」ものではなく、RTO / RPO の要件とコストのバランスで決めます。

  • 単一AZ。コストは最小。インスタンス障害時は復旧まで停止。開発環境向け。
  • マルチAZ(RDS)/Aurora のリーダー配置。AZ障害に耐えます。Aurora では、リーダーをライターと別AZに置くことが実質的なマルチAZ構成になります。リーダーが0台の Aurora クラスターは、ライター障害時に新インスタンス起動待ちが発生する点に注意してください。
  • RDS マルチAZ DBクラスター(2つのリーダブルスタンバイ)。RDS(通常)側の選択肢で、フェイルオーバー時間の短縮と読み取りスケールを両立します。
  • クロスリージョン。Aurora Global Database やリードレプリカによるリージョン障害対策。RPO は通常秒単位ですが、ゼロではありません。コストとネットワーク転送料が加わります。

判断の起点は「そのシステムが何分止まると、いくらの損失/どんな責任問題になるか」です。ここが曖昧なまま構成だけ豪華にすると、運用の複雑さだけが増えて、かえって切り替え手順のミスを招きます。フェイルオーバーを実際に手動実行して、アプリが復帰することを確認するところまでやって、はじめてその構成に意味が出ます。

# 手動フェイルオーバーで切り替え挙動を検証する(検証環境で)
aws rds failover-db-cluster \
  --db-cluster-identifier my-aurora-cluster \
  --target-db-instance-identifier my-aurora-instance-2

この記事のテーマをさらに学べる本

EC2・IAM・RDSの運用やバックアップ/リストア、監査までを体系立てて解説しており、RDS運用の実務知識を広げられます。

AWS運用入門
AWS運用入門佐竹 陽一/山崎 翔平/小倉 大/峯 侑資
楽天ブックスで詳しく見る Yahoo!ショッピングで見る

この記事のテーマをさらに学べる本

データの扱いから運用方法、SQLまでを図解で解説しており、RDS・Auroraの前提となるDB基礎の整理に使えます。

楽天ブックスで詳しく見る Yahoo!ショッピングで見る

❓ よくある質問

Aurora のフェイルオーバーは短時間で終わったのに、その後しばらく遅いのはなぜ?

昇格したインスタンスのバッファプールに、ライターが扱っていた書き込み系のホットなページが乗っていないためです。ストレージからのページ読み取りが増え、Aurora PostgreSQL では待機イベント IO:DataFileRead の増加として観測できます。フェイルオーバー後 5〜15分で徐々に改善するならウォームアップ中と判断してよく、恒常的に改善しない場合はパラメータ、接続先の偏り、インスタンスクラスの差などを疑います。

「too many connections」が出たら max_connections を上げればよい?

多くの場合は悪手です。PostgreSQL は接続ごとにプロセスを生成するため、アイドル接続が大量にメモリとコンテキストスイッチを消費し、上げた結果として全体が遅くなることがあります。まず pg_stat_activity で状態別の内訳を確認し、idle が多ければアプリ側のプール設定過剰、idle in transaction が長時間残っていればトランザクション制御のバグ(コミット漏れ、例外時のロールバック漏れ)を疑います。

RDS Proxy を入れれば性能問題は解決できる?

接続の確立・切断が高頻度なケースや、Lambda のように接続を持続できない環境、フェイルオーバー時のエラー時間短縮には効果がありますが、遅いクエリによる詰まりには効きません。Proxy は接続を束ねるだけで SQL は速くならず、待ち行列が Proxy 側に移動して見えにくくなります。長時間トランザクションや一時テーブル、プリペアドステートメント、セッション変数の利用があると接続ピン留めが発生し、多重化の効果が失われます(DatabaseConnectionsCurrentlySessionPinned で確認)。

大量に DELETE したのにストレージ使用量が減らないのは異常?

MVCC の帰結であり正常な挙動です。削除された行はデッドタプルになり、VACUUM は基本的にファイル内に空き領域を作るだけで OS へ容量を返しません(テーブル末尾の連続した空き領域のみ切り詰められます)。物理的に縮めたい場合は VACUUM FULL、pg_repack 拡張、テーブルの再作成+入れ替えという選択肢があり、まずはテーブル/インデックスサイズの上位を測ってどこが太っているかを特定します。

📚 学習の進め方:この先どの記事から読むか

自分の症状から入る:本記事の各トピックと個別記事の対応マップ

いま困っている事象から、この記事のどのセクションに戻ればよいかを整理します。

症状 見るべきセクション 最初に確認すること
接続エラーが大量に出る 接続まわりのトラブル pg_stat_activity の state 内訳、RDSイベント
フェイルオーバー後にエラーが続く DNSキャッシュとコネクションプール JVM/DNS TTL、プールの生存確認設定
特定のクエリだけ遅い 実行計画が突然変わる EXPLAIN (ANALYZE, BUFFERS) の推定vs実測
全体的に遅い 待機イベントから読む Performance Insights の DBロードと待機内訳
一部の処理が固まる ロック競合とブロッキング pg_blocking_pids() でブロッカー特定
数か月かけて遅くなった MVCC とテーブル肥大化 n_dead_tup、last_autovacuum
書き込みができなくなった トランザクションID周回 age(datfrozenxid)
ストレージが急に減った 一時領域枯渇 FreeLocalStorage、log_temp_files
レプリカを増やしても速くならない リードレプリカに振っても速くならない 待機イベントが Lock:* か CPU か
バージョンアップが不安 Blue/Greenデプロイ クローンでのリハーサル、拡張の対応状況

このマップの各行は、それぞれ独立した深掘りが必要なテーマです。今後、フェイルオーバー検証、Performance Insights による待機イベント分析、autovacuum チューニング、Blue/Green デプロイ手順といった個別記事で掘り下げていきます。

検証環境で再現する習慣をつける(小さいインスタンスでできること)

💡 RDS/Aurora の学習で最も効率が良いのは、小さいインスタンスで意図的に問題を起こしてみることです。多くの現象は、db.t4g.medium 程度の環境でも十分に再現できます。

たとえば、

  • max_connections を小さく設定して、接続枯渇時のエラーメッセージを実際に見る
  • 2つのセッションで同じ行を UPDATE して、pg_blocking_pids() でブロッカーを特定する手順を練習する
  • 1つのセッションで BEGIN; したまま放置し、別セッションで大量更新した後に n_dead_tup が減らないことを確認する
  • work_mem を極端に小さくして、大きな ORDER BY で一時ファイルが発生することをログで確認する
  • 手動フェイルオーバーを実行して、アプリからの接続がどう振る舞うかを観察する

避けたいのは、障害時に初めてコマンドを調べる状況です。平常時に一度でも打ったことのあるSQLは、障害時にも打てます。上で紹介した確認用SQLを手元のスニペットとしてまとめておくだけでも、初動の速度が変わります😇

⚠️ 検証環境は使い終わったら停止・削除する運用を徹底してください(RDS の「停止」は最大7日で自動起動する点に注意)。

公式ドキュメント・リリースノートの追い方

RDS/Aurora は機能追加とバージョン更新のペースが速く、書籍やブログの情報は短期間で古くなります。一次情報を追う習慣が必須です。

  • Amazon Aurora / RDS ユーザーガイド。仕様の基準。特に「エンジンバージョンごとの機能サポート表」「パラメータ一覧」は頻繁に参照します。
  • エンジンのリリースノート。Aurora PostgreSQL / Aurora MySQL それぞれにバージョン別のリリースノートがあり、修正されたバグや改善点が列挙されています。「アップグレードしたら直った/変わった」の答えはここにあります。
  • コミュニティ版のリリースノート。PostgreSQL / MySQL 本家の変更点。Aurora の挙動の大半はここに由来します。
  • AWS の What’s New / RDS の更新履歴。新機能の把握。

💡 ドキュメントを読むときのコツは、「この記述はエンジン共通か、Aurora 固有か、バージョン固有か」を常に意識することです。Aurora PostgreSQL と RDS for PostgreSQL では、同じ PostgreSQL でもストレージ層が違うため、パラメータの推奨値やサポートされる拡張が異なります。ネット上の情報を適用する前に、自分の環境の組み合わせに当てはまるかを確認する癖をつけてください。

資格学習(SAA/DBS)と実運用スキルの重なりと、重ならない部分

AWS 認定(Solutions Architect – Associate、Database – Specialty 系)の学習範囲と、この記事で扱った実運用スキルは、重なる部分と重ならない部分がはっきりしています。

重なる部分は次のあたりです。

  • マルチAZ・リードレプリカ・Global Database といった可用性構成の選択
  • バックアップ/PITR/スナップショットの仕組みと制約
  • エンドポイントの種類と役割
  • RDS Proxy、Serverless の適用場面
  • 暗号化、IAM認証、Secrets Manager 連携といったセキュリティ設計

これらは「どの構成を選ぶか」という設計判断であり、資格学習で体系的に押さえるのが効率的です。

重ならない部分は次のあたりです。

  • 待機イベントを読んでボトルネックを特定する力
  • 実行計画を読み、インデックス設計を判断する力
  • MVCC・VACUUM・UNDO といったエンジン内部の理解に基づくトラブル対応
  • 長時間トランザクションやロック競合といった、アプリ実装に踏み込む問題解決

こちらは、資格学習だけでは身につきにくく、エンジン本体の知識と実際の調査経験が必要になる領域です。逆に言えば、この領域はクラウドが変わっても陳腐化しにくい資産になります。

現実的な進め方としては、資格学習で「AWSのサービスとしての選択肢」を網羅し、並行してエンジン内部(PostgreSQL なら MVCC・VACUUM・実行計画、MySQL なら InnoDB のバッファプール・UNDO・ロック)を掘る、という二方向の学習が効率的です。この記事の各セクションは、その両方への入口として使えるように構成しています。気になったトピックから、検証環境で手を動かして確かめてみてください。

このテーマの記事一覧(10本)

  1. AWSトラブル解決Serverless v2のmax_connections未反映

    Aurora Serverless v2で最大ACUを変えてもmax_connectionsが変わらないのは、staticパラメータのため再起動が必要だから。計算式の仕組み、SHOWとpending-rebootでの切り分け、複数インスタンス構成での再起動手順までまとめます。

  2. AWSトラブル解決RDS スナップショット復元が遅い原因と対処

    RDS(非Aurora)のスナップショットやPITRから復元したDBが遅いのは、EBSの遅延ロード(ファーストタッチペナルティ)が主因です。CloudWatchでの切り分け、pg_prewarm等で先に読み込む対処、復旧訓練への組み込み方まで解説します。

  3. AWSトラブル解決Aurora I/O 料金の見積もり方|実測で出す手順

    Aurora の I/O 料金を見積もる方法を解説。読み取り 8KB・書き込み 4KB というカウント単位の仕組みから、VolumeReadIOPs / VolumeWriteIOPs での実測手順、Standard と I/O-Optimized の選び分けまで、移行前の試算で迷わない判断基準をまとめます。

  4. AWSトラブル解決Aurora インスタンスクラス変更のダウンタイム最小化手順

    Aurora PostgreSQL でインスタンスクラスを変更する際、書き込み停止を最短にする順序を解説。Cluster Cache Management 有効時の事前確認、リーダー変更→手動フェイルオーバー→旧ライター変更の手順、よくある誤解と検証方法までまとめます。

  5. AWSトラブル解決CloudWatch アラームを時間帯で抑制する手順

    バッチ処理中のCPU高騰でCloudWatchアラームが鳴り続ける問題に対し、時間帯を限定して通知だけを止める方法を解説。アラームアクションの無効化とEventBridge Schedulerによる自動化、誤解しやすい挙動、閾値見直しやAurora Serverless v2など代替策まで。

  6. AWSトラブル解決Auroraレプリカラグ監視|AuroraReplicaLag

    Aurora PostgreSQL のリードレプリカ同期遅延を AuroraReplicaLag で監視する方法を解説。共有ストレージ方式ゆえのラグの正体、CloudWatch/CLI での取得手順、IAM 権限、アプリ要件から閾値を逆算する考え方、ラグ急増時の切り分け順までまとめます。

  7. AWSトラブル解決RDS Multi-AZ フェイルオーバー原因の確認手順

    RDS の Multi-AZ で突然フェイルオーバーが起きたとき、原因をどう特定するか。イベント履歴と API 呼び出し履歴による切り分け手順、基盤ホスト異常時の挙動、ロールバックとデータ整合性の誤解、接続リトライや通知設定による再発対策までまとめます。

  8. AWSトラブル解決RDS 証明書ローテーションで再起動される原因と対処

    操作していないのに RDS が再起動された——原因はサーバー証明書の自動ローテーションかもしれません。イベント履歴とメンテナンスウィンドウの確認手順、エンジンバージョンが再起動を伴うかの判定コマンド、再起動タイミングを自分で選ぶ手動適用、通知設定までを実務目線でまとめます。

  9. データベースAurora PostgreSQLスナップショット復元と統計情報

    Aurora PostgreSQL のスナップショットを別環境に復元すると ANALYZE の統計情報は残るのか。pg_statistic と pg_stat_user_tables の違い、復元後の確認クエリ、ANALYZE が必要になるケースを実機検証ログ付きで整理します。

  10. データベースPostgreSQL論理レプリケーションのWAL保持とスロット運用

    PostgreSQL / Aurora PostgreSQLの論理レプリケーションでWALが消えない原因を、restart_lsnとconfirmed_flush_lsnの仕組みから整理。pg_replication_slotsでの切り分け、max_slot_wal_keep_sizeの判断、3スロット検証ログまで解説します。