About

RDSをMulti-AZにする前に知っておくこと — 料金は2倍、スタンバイは読めない、順番が大事

Single-AZの本番DBをMulti-AZに移して踏んだもの。一度も効いていなかったterraformの分岐、止められないスナップショット、ストレージまで2倍

本番のRDS for PostgreSQLをSingle-AZからMulti-AZに移した。この記事を読めば、Multi-AZが実際に買ってくれるもの(障害・メンテナンス時の短い停止)と買ってくれないもの(読み取り性能)、料金がどこで2倍になるか、切り替えをどの順番でやれば痛くないかを決められる。

きっかけはレビューで受けた質問ひとつだった。「今までMulti-AZにしていなかった理由はありますか?」調べてみたら、理由はなかった。

有効にするコードはあった。一度も真にならなかっただけ

terraformには、本番だけMulti-AZにする分岐がちゃんとあった。

locals {
  rds_multi_az = var.stage == "production"
}

variable "stage" {
  default = "staging"  # 本番のディレクトリなのにstaging
}

tfvarsもなく、Makefileも -var stage=... を渡していなかった。だから rds_multi_az はずっとfalse。初期構築のときのdefaultが何年も生き残っていた。

条件付きで有効になる設定は、その条件が本当に真になるかまで確かめる。コードに書いてあることと、適用されていることは別物だ。

もうひとつあった。2か月近くapplyがない間に、ストレージのオートスケーリングが allocated_storage をコードの値の2倍まで広げていた。そのままapplyしていたら、terraformはストレージを縮めようとして失敗していたはずだ。RDSのストレージは増やすことしかできない。terraform plan を定期的に回してさえいれば、どちらもとっくに見えていた。

Multi-AZがやることは2つだけ

別のAZ(同じリージョン内の離れたデータセンター群)にスタンバイのインスタンスを置き、同期でレプリケーションする。

  • 書き込みはプライマリとスタンバイの両方に記録されてからコミットされる

  • スタンバイは読めない。待っているだけ

2つ目がよく誤解される。AWSのドキュメントにも「読み取りのスケーリング手段ではない、スタンバイで読み取りトラフィックは受けられない」と書いてある。冗長化したから速くなる?ならない。コミットのたびにAZを1往復するので、書き込みのレイテンシはむしろ少し増える。普段は体感しないが、大量のINSERT・UPDATEを回すバッチでは積み上がる。

Multi-AZ instance this post Primary (AZ-a) sync Standby (AZ-b) reads: no auto failover: yes price: instance x2, storage x2 Read replica for read scaling Primary async Replica reads: yes auto failover: no (promote by hand) price: one more instance Multi-AZ DB cluster availability + reads Writer semi-sync Reader Reader reads: yes (2 readers) auto failover: yes storage x3, NVMe classes only

買えるもの① 障害時の自動フェイルオーバー

AZ・インスタンス・ストレージに問題が起きると、スタンバイが昇格し、エンドポイントのDNSが新しいプライマリを指す。アプリの設定は変えなくていい。ドキュメントではフェイルオーバーは通常60〜120秒だ。

ただ、その瞬間に既存の接続はすべて切れる。アプリに再接続・リトライがなければ、その1〜2分の間エラーがそのままユーザーに出る。Multi-AZを有効にしただけでアプリが丈夫になるわけではない。

買えるもの② メンテナンスの停止が短くなる — 実務ではこれがいちばん大きい

Single-AZでインスタンスクラスを変えると、停止 → リサイズ → 起動になる。うちは約3分止まった。

Multi-AZなら、OSパッチはスタンバイ先行 → 昇格 → 旧プライマリの順に当たる。ドキュメントではそのフェイルオーバーは通常1分未満。クラス変更についてもドキュメントは「Multi-AZならダウンタイムを最小にできる」と書いている。うちはクラスをSingle-AZのうちに変えたので、Multi-AZ側の数字は測っていない。

最初はエンジンのマイナーバージョンアップも同じように短くなると思っていた。違った。ドキュメントを読み直すと、エンジンのアップグレードはプライマリとスタンバイを同時に変えるので、両方止まる。エンジンアップグレードの停止を減らしたいならBlue/Green Deploymentsの方を見る。

それでも、OSパッチとクラス変更が怖くなくなるだけで大きい。ダウンタイムが怖くてパッチを先送りし、溜まったパッチがもっと大きな作業になる、という悪循環から抜けられる。

買えるもの③ バックアップでプライマリが止まらない

Single-AZでスナップショットを取ると、数秒から数分I/Oが止まる(ドキュメント)。PostgreSQL・MySQLのMulti-AZはスタンバイから取るので、プライマリのI/Oは止まらない。

今回これを正面から食らった。作業前に保険で手動スナップショットを取ったら、ボリュームの帯域がすでに張り付いていて、数十GBに40分ほどかかった。その間インスタンスは backing-up で、変更リクエストは拒否された。

InvalidDBInstanceState: Database instance is not in available state.
InvalidDBSnapshotState: Cannot delete the snapshot because it is not
  currently in the available or failed state.

走っているスナップショットを削除して止めようとしても、2行目のように拒否される。押さえておいた作業時間のうち40分を、ただ待った。

料金 — インスタンスもストレージも2倍

AWSの公開価格表(東京リージョン・PostgreSQL・オンデマンド、2026-10-29版)から引いた。月730時間で計算。

Single-AZ Multi-AZ
db.t3.large $163.52 $327.04
db.r6g.xlarge $394.20 $787.67
gp3ストレージ $0.138/GB・月 $0.276/GB・月

インスタンスだけ見て見積もると外れる。ストレージも2倍だ。うちの最初の見積もりもインスタンスの値だけで出していて、実際よりだいぶ低かった。

3つのうちどれを買うか — Multi-AZ・リードレプリカ・Multi-AZ DBクラスター

スタンバイで読む レプリケーション 自動フェイルオーバー 東京のgp3ストレージ
Multi-AZインスタンス 不可 同期 あり $0.276(2倍)
リードレプリカ 可 非同期 なし(手動で昇格) レプリカのインスタンス分が別に
Multi-AZ DBクラスター 可(リーダー2台) 準同期 あり $0.414(3倍)

3つは排他ではない。止まらないことが目的ならMulti-AZ、読み取りを分けたいならリードレプリカ。両方要るなら併用するか、クラスターを検討する。ただしクラスターはr6gd・m6gdのようなローカルNVMe付きのクラスしか選べないので、今のクラスをそのまま持っていくことはできない。

切り替えの順番 — スペックアップが先、Multi-AZは後

有効にすること自体は無停止だ。

aws rds modify-db-instance --db-instance-identifier <id> --multi-az --apply-immediately

--apply-immediately を付け忘れると、次のメンテナンスウィンドウまで待つ。夜に押さえた作業がまるごと無駄になる。

無停止だから軽い、というわけではない。RDSはプライマリのボリュームのスナップショットからスタンバイのボリュームを作り、そのボリュームは裏でデータを埋めていく(ドキュメント)。すでにI/Oが張り付いているインスタンスで有効にすると、この作業がいつまでも長引き、その間サービスも遅くなる。スナップショットひとつに40分かかったのがその証拠だった。

だから順番をこうした。

  1. 停止の時間を取ってクラスを上げる(Single-AZ、約3分停止) — まず帯域を確保
  2. そのあとMulti-AZを有効にする(無停止) — 同期が短く、痛くない

進み具合はこう見る。

aws rds describe-db-instances --db-instance-identifier <id> --query 'DBInstances[0].{Status:DBInstanceStatus,MultiAZ:MultiAZ,Secondary:SecondaryAvailabilityZone,Storage:StorageOperationStatus}'

SecondaryAvailabilityZone が埋まればスタンバイはできている。ステータスが available に戻っても、スタンバイのボリュームはまだ裏で埋まっている途中のことがある。それは StorageOperationStatus が Initializing かどうかで見る。うちは数十GBで20〜40分かかった。

実はAWSのドキュメントは、本番インスタンスをその場で変えるより、リードレプリカを作ってMulti-AZにしてから昇格させる道を勧めている。書き込みに敏感なサービスなら、そちらを先に検討する価値がある。うちはその場で変えて、代わりに上の順番で帯域を先に空けておいた。

T系から移るとダッシュボードにNo dataが出る

CPUCreditBalance はバースト可能(T系)インスタンス専用のメトリクスなので、r6gに移ると出なくなる。ダッシュボードのウィジェットが「No data」になるが、故障ではない。このメトリクスにアラームを掛けていたら誤検知になるので、先に片付けておく。

有効にするか、しないか

有効にする: 数分の停止が売上・信頼に直結するサービス。パッチ・スペック変更を定期的にやる必要があるのに、ダウンタイムが怖くて先送りしているところ — これだけでも元が取れる。バックアップ中のI/O停止が問題になる規模。

しない: dev・staging(料金が2倍になるだけ)。数分止まってもよく、復旧時間に余裕がある社内システム。

覗くと一緒に出てくるもの

Multi-AZを検討しているうちに、同じインスタンスから初期構築のときの設定がぞろぞろ出てきた。

  • バックアップ保持1日 — terraformに backup_retention_period = 1 がハードコードされていた。ポイントインタイムリカバリ(PITR)の範囲が24時間しかなく、1日過ぎて気づいた事故は戻せない

  • ストレージ暗号化off — 有効にするにはスナップショットのコピー → 復元 → エンドポイント切り替えで、別に停止が要る。その場では変えられない

  • オートスケーリングの上限に到達 — 割り当てがすでに上限と同じで、もう広がる余地がなかった

インフラを一度ちゃんと覗くと、こういうものが必ず出てくる。Multi-AZの検討はそのきっかけにちょうどいい。

本番DBの冗長化やスペック変更の順番、terraformと実際の状態がずれたインフラの整理で迷っているなら、https://www.negabaro.com/contact から聞いてほしい。

動作フロー

1

terraformのMulti-AZの条件が本当に真になるか、planに差分がないかを先に見る

2

停止の時間を取ってインスタンスクラスを上げる(Single-AZは停止 → リサイズ → 起動)

3

--multi-az --apply-immediately でMulti-AZを有効にする(無停止、スナップショットからスタンバイのボリュームを作成)

4

SecondaryAvailabilityZoneとStorageOperationStatusでスタンバイの準備が終わったかを見る

5

T系専用メトリクス(CPUCreditBalance)のウィジェット・アラームを片付ける

メリット

  • ✓ 障害時に自動フェイルオーバー、エンドポイントはそのまま
  • ✓ OSパッチはスタンバイ先行なので、停止はフェイルオーバーの分だけ
  • ✓ バックアップはスタンバイから取るのでプライマリのI/Oが止まらない(PostgreSQL・MySQL)

デメリット

  • ✗ インスタンスもストレージも料金が2倍
  • ✗ スタンバイは読めない — 読み取り性能は変わらない
  • ✗ フェイルオーバーで接続が切れる。エンジンのアップグレードは相変わらず停止する

ユースケース

数分の停止が売上・信頼に直結する本番DB ダウンタイムが怖くてOSパッチ・クラス変更を先送りしてきたDB バックアップ中のI/O停止が問題になる規模
← ボット対策はヘッダーひとつではない — 守る側から見た5つの層 ECS Execのセッションログが1行も残らないとき — まずイメージにscriptがあるかを見る →