# PostgreSQL ストリーミングレプリケーションと高可用性（HA・同期/非同期・リードレプリカ・フェイルオーバー・v18対応）

> PostgreSQL の高可用性(HA)をストリーミングレプリケーションで実現する実践ガイド。物理レプリケーションの構築手順、同期/非同期(synchronous_commit)の耐久性とレイテンシのトレードオフ、ホットスタンバイでのリードレプリカとレプリケーション競合、PostgreSQLコアが自動フェイルオーバーを提供しない事実とSTONITH、レプリケーションスロットによるWAL保持、PostgreSQL 18の改善までを公式ドキュメントに忠実に解説します。

- 公開日: 2026-06-23
- 著者: 友田 陽大
- タグ: PostgreSQL, アーキテクチャ設計
- URL: https://tomodahinata.com/blog/postgresql-streaming-replication-high-availability-failover-guide

## 要点

- 物理ストリーミングレプリケーション＝プライマリのWALをスタンバイへ逐次ストリームし、ほぼ最新の複製を待機させる。クラスタ全体・同一メジャー版が前提
- 同期/非同期は耐久性とレイテンシのトレードオフ。既定は非同期(synchronous_standby_namesが空)。synchronous_commitは off→local→remote_write→on→remote_apply の順に強く・遅くなる
- ホットスタンバイで読み取りを逃がせるが、スタンバイの長時間クエリとプライマリのVACUUMが競合する。max_standby_streaming_delay か hot_standby_feedback で調整(後者はプライマリ肥大化の副作用)
- 最重要：PostgreSQLコアは自動フェイルオーバーを提供しない(公式明記)。検知・昇格・IP切替・スプリットブレイン防止(STONITH)はPatroni等の外部ツールかマネージドで組む
- WAL保持はレプリケーションスロットが安全だが、スタンバイ停止中にpg_walが無限増大するリスク。max_slot_wal_keep_sizeで上限を。PG18はidle_replication_slot_timeout等を追加

---

サーバーは必ず壊れます。ディスクは飛び、AZは落ち、カーネルはパニックする。**単一のPostgreSQLに全てを賭ける**のは、いつか必ず来る障害に「ダウンタイムで払う」契約をしているのと同じです。

高可用性（HA）の基本は**レプリケーション**——常に最新に近い複製を別サーバーに用意し、いざというとき切り替える。この記事は、PostgreSQL の物理ストリーミングレプリケーションでHAを組む方法と、**最も誤解される「自動フェイルオーバー」の現実**を、公式ドキュメントに忠実に解説します。[本番運用ガイド §3](/blog/postgresql-production-operations-guide)の深掘りです。

> **この記事のルール**：レプリケーションの仕様・パラメータ既定値・PostgreSQL 18 の変更点は **PostgreSQL 18 公式ドキュメント（2026年6月時点）** に基づきます。フェイルオーバー自動化ツール（Patroni 等）は、公式が「外部ツール」と総称する範囲で、具体名はコミュニティ知識として注記します。

---

## 1. HAの選択肢：物理 vs 論理 vs 共有ディスク

PostgreSQL のHAには複数のアプローチがあります（公式の比較）。

| 方式 | 仕組み | 向く場面 | 制約 |
| --- | --- | --- | --- |
| **物理ストリーミング** | WALをバイト単位でスタンバイへ | クラスタ全体のHA/DR、リードレプリカ | 同一メジャー版・全体複製のみ |
| **論理レプリケーション** | 行変更を publish/subscribe で | テーブル単位・**版跨ぎ**・双方向 | 競合解決が必要・DDL非複製（[別記事](/blog/postgresql-logical-replication-cdc-zero-downtime-upgrade-guide)） |
| **共有ディスク** | 単一ストレージを共有 | — | ストレージ自体がSPOF・複製はされない |

本記事の主役は**物理ストリーミングレプリケーション**。公式の説明：

> ストリーミングレプリケーションは、ファイルベースのログシッピングより最新に保てる。スタンバイがプライマリに接続し、プライマリは**WALレコードを生成され次第ストリームする。WALファイルが満杯になるのを待たない**。

「ほぼリアルタイムのバイト単位の複製」です。物理なのでスタンバイはプライマリと**ブロック単位で同一**——同一メジャーバージョンが前提です。

---

## 2. 物理ストリーミングレプリケーションの構築

最小構成の流れです。

### 2.1 プライマリ側の設定

```ini
# primary postgresql.conf
wal_level = replica            # replica 以上（既定）
max_wal_senders = 10           # 同時WAL送信プロセス数（既定 10）
max_replication_slots = 10     # レプリケーションスロット数（既定 10）
```

```sql
-- 専用のレプリケーションロール（公式推奨：REPLICATION + LOGIN の最小権限）
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD '***';
-- スロットを作っておくと、スタンバイが遅れてもWALを保持してくれる（§5）
SELECT pg_create_physical_replication_slot('standby1');
```

```text
# primary pg_hba.conf（replication は専用キーワード。TLS強制＋SCRAM）
hostssl replication replicator 10.0.0.0/24 scram-sha-256
```

### 2.2 スタンバイ側の構築

```bash
# プライマリからベースバックアップを取得し、復旧設定まで書き出す（-R）
pg_basebackup -h primary.internal -U replicator -D /var/lib/postgresql/18/main \
  -Ft -z -X stream -c fast -R -S standby1
#   -R: standby.signal と primary_conninfo を自動生成 / -S: 上で作ったスロットを使う
```

`-R` が生成する `postgresql.auto.conf` には、こう書かれます。

```ini
primary_conninfo = 'host=primary.internal user=replicator passfile=... sslmode=verify-full'
primary_slot_name = 'standby1'
```

そして `standby.signal` ファイルがあるため、起動するとスタンバイモードで立ち上がり、プライマリのWALを受信し続けます。`hot_standby`（既定 `on`）により、**スタンバイで読み取りクエリを実行できます**（§4）。

```bash
pg_ctl -D /var/lib/postgresql/18/main start
```

---

## 3. 同期 vs 非同期：耐久性とレイテンシのトレードオフ

「コミットを返す前に、スタンバイの複製を待つか」が**同期/非同期**です。既定は**非同期**——公式曰く「`synchronous_standby_names` に同期スタンバイ名が指定されていなければ、同期レプリケーションは無効で、コミットは複製を待たない。**これが既定**」。

`synchronous_commit` の値で耐久性の段階を選びます（既定 `on`）。下にいくほど**強く・遅い**。

| 値 | コミットが待つもの | 守られるもの |
| --- | --- | --- |
| `off` | 何も待たない（ローカルflushすら） | 最速。クラッシュで直近の数件を失い得る（破損はしない） |
| `local` | ローカルのディスクflushのみ | 単一ノードの耐久性 |
| `remote_write` | スタンバイがWALを**受信・OS書き込み** | スタンバイのPostgreSQLクラッシュには耐える（OSクラッシュは別） |
| `on`（既定） | スタンバイがWALを**ディスクflush** | プライマリ＋全同期スタンバイが同時破損しない限り失わない |
| `remote_apply` | スタンバイがWALを**適用（可視化）** | スタンバイのクエリに即反映。最も遅い |

```ini
# 「1件も失えない」要件：同期レプリケーション
synchronous_standby_names = 'standby1'   # このスタンバイの確認を待つ
synchronous_commit = on                   # 受信＋flush を待つ
```

> **設計判断**：金融・決済のように「コミット＝絶対に消えない」が要件なら同期（`on` 以上）。一般的なWebアプリは**非同期で十分**なことが多い（レイテンシ優先、わずかなデータ損失リスクを許容）。`remote_apply` はリードレプリカに即反映したいときだけ——commit遅延が大きくなります。

---

## 4. リードレプリカ：読み取りを逃がす（と、その代償）

ホットスタンバイは**読み取り専用クエリ**を受けられます。公式：「`hot_standby` が真のとき、復旧が一貫状態に達すれば接続を受け付ける。**すべての接続は厳密に読み取り専用**（一時テーブルへの書き込みすら不可）」。重い集計・レポートをレプリカに逃がし、プライマリを書き込みに専念させる定番構成です。

ただし**レプリケーション競合**という固有の問題があります。公式：「スタンバイのクエリとWAL再生の競合の最も一般的な原因は『早期クリーンアップ』。…プライマリ側のクリーンアップ（VACUUM）が、**スタンバイのトランザクションがまだ見ている行バージョンを削除**してしまう可能性がある」。

スタンバイは「WAL再生を遅らせる」か「**競合クエリをキャンセル**する」を迫られます。2つのノブで調整します。

```ini
# ノブ1：競合時、どれだけWAL適用を遅らせるか（超えたらクエリをキャンセル）
max_standby_streaming_delay = 30s

# ノブ2：スタンバイの実行中クエリをプライマリにフィードバックし、VACUUMの早期クリーンを抑止
hot_standby_feedback = on    # 既定 off
```

公式の警告：`hot_standby_feedback = on` は競合を消せるが「**プライマリ側で不要行のクリーンアップが遅れ、テーブル肥大化を招き得る**」。トレードオフです——**長い分析クエリをレプリカで回すなら `hot_standby_feedback`、プライマリの肥大化を避けたいなら `max_standby_streaming_delay`** で許容遅延を設計します。

---

## 5. WAL保持の安全装置：スロット vs wal_keep_size

スタンバイが遅れた/落ちた間に、プライマリが必要なWALを消すと、**複製が壊れます**。これを防ぐ2つの仕組み。

- **`wal_keep_size`**（既定 0）：`pg_wal` に保持する**最小サイズ**。これを超えて遅れたスタンバイは、必要なWALを失い接続が切れる。固定バッファに過ぎない。
- **レプリケーションスロット**：スタンバイが消費するまでWALを**確実に保持**する。より安全。

スロットの方が安全ですが、**致命的な副作用**があります。公式（`max_slot_wal_keep_size`、既定 -1）：「-1 なら、レプリケーションスロットは**無制限のWALを保持し得る**」。つまり**スタンバイが落ちたまま放置すると、プライマリの `pg_wal` が無限に膨れてディスクを食い潰す**。

```ini
# スロットは使うが、保持上限を設けて pg_wal の暴走を防ぐ
max_slot_wal_keep_size = 64GB   # これを超えたら、遅れたスロットは無効化（複製は切れるがDBは守る）
```

> **運用の鉄則**：スロットは「便利だが危険」。**必ず `max_slot_wal_keep_size` で上限を設け**、`pg_replication_slots` の遅延を監視します。PostgreSQL 18 は **`idle_replication_slot_timeout`** を追加し、長時間アイドルなスロットを自動無効化できるようになりました。

---

## 6. フェイルオーバー：PostgreSQLは自動でやってくれない

ここが**最も誤解される点**です。スタンバイを「昇格」させる操作自体は簡単。

```sql
-- スタンバイをプライマリに昇格（手動フェイルオーバー）
SELECT pg_promote();
-- または: pg_ctl promote -D <datadir>
```

しかし**「いつ昇格するか」を誰が決めるのか**。公式が明確に述べます。

> PostgreSQL は、プライマリの障害を検知してスタンバイに通知するために**必要なシステムソフトウェアを提供しない**。

つまり**自動フェイルオーバーはコアに無い**。さらにスプリットブレイン（両方がプライマリだと思い込む）の危険：

> 古いプライマリに「お前はもうプライマリではない」と知らせる仕組みが必要だ。これは **STONITH（Shoot The Other Node In The Head）** として知られ、両系がプライマリと思い込んで**データ損失**に至る事態を避けるために必要である。

公式は「そうしたツールは多数存在し、OS機能（IPアドレス移行など）とよく統合されている」と述べるにとどめます。実際には次の外部ツールで組みます（コミュニティ標準）：

- **Patroni**（etcd/Consul と連携、デファクト）
- **repmgr**、**pg_auto_failover**

> **これがマネージドの最大の価値**（[運用ガイド §8](/blog/postgresql-production-operations-guide)）。RDS/Aurora の Multi-AZ は、この「**障害検知＋自動昇格＋エンドポイント切替＋STONITH相当**」を代行します。自前運用なら、レプリケーション構築よりも**この自動フェイルオーバー基盤の構築・運用**が本当のコストです。

---

## 7. 監視：遅延とスロットを見る

```sql
-- プライマリ側：各スタンバイの遅延（write/flush/replay lag）
SELECT application_name, state, sync_state,
       write_lag, flush_lag, replay_lag
FROM pg_stat_replication;

-- スロットの滞留（restart_lsn が進まない＝WALが溜まる）
SELECT slot_name, active, wal_status,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots;
```

`replay_lag` が継続的に増える＝スタンバイが追いつけていない（I/O不足やレプリケーション競合）。`retained_wal` が増え続ける＝スロットが滞留し `pg_wal` が膨張中——上限とアラートを必ず設定します。

---

## 8. まとめ

- **物理ストリーミングレプリケーション**でHAを組む：`wal_level=replica`、専用ロール、`pg_basebackup -R`、スロット。
- **同期/非同期は耐久性とレイテンシのトレードオフ**。既定は非同期。`synchronous_commit` を要件で選ぶ。
- **リードレプリカ**は読み取りを逃がせるが、**レプリケーション競合**に注意（`max_standby_streaming_delay` / `hot_standby_feedback`）。
- **コアは自動フェイルオーバーを持たない**（公式明記）。検知・昇格・STONITHは **Patroni 等の外部ツールかマネージド**で。
- **スロットは安全だが暴走する**——`max_slot_wal_keep_size` で上限を設け、遅延を監視。

「障害で止めない」が固まったら、次は「**変更でも止めない**」——無停止のスキーマ変更、そして版跨ぎの[論理レプリケーション＆ゼロダウンタイム・アップグレード](/blog/postgresql-logical-replication-cdc-zero-downtime-upgrade-guide) へ。

---

### 参考（PostgreSQL 18 公式ドキュメント）

- [26.1. Comparison of Different Solutions](https://www.postgresql.org/docs/18/different-replication-solutions.html)
- [26.2. Log-Shipping Standby Servers（ストリーミング構築）](https://www.postgresql.org/docs/18/warm-standby.html)
- [26.3. Failover](https://www.postgresql.org/docs/18/warm-standby-failover.html)
- [26.4. Hot Standby](https://www.postgresql.org/docs/18/hot-standby.html)
- [19.6. Replication（synchronous_commit / スロット）](https://www.postgresql.org/docs/18/runtime-config-replication.html)
