はじめに
本番環境でデータベースを運用する際、単一障害点(Single Point of Failure)の排除は避けて通れない課題です。プライマリサーバーに障害が発生した場合、サービスが完全に停止してしまうリスクを最小化するために、PostgreSQLではレプリケーション機能を使った高可用性構成を構築できます。
本記事では、PostgreSQLのストリーミングレプリケーションを中心に、プライマリ・スタンバイ構成の基本概念から実際の構築手順、同期・非同期レプリケーションの違い、フェイルオーバーの考え方まで体系的に解説します。
この記事を読むことで、以下のことができるようになります。
- PostgreSQLにおけるレプリケーションの種類と特徴を説明できる
- ストリーミングレプリケーションの仕組みを理解できる
- プライマリ・スタンバイ構成を構築できる
- 同期・非同期レプリケーションの使い分けを判断できる
- フェイルオーバーの基本的な流れを理解できる
前提条件
- PostgreSQL 14以降がインストールされていること
- Linux(Ubuntu/CentOS)の基本的なコマンド操作ができること
- PostgreSQLの基本操作(psqlでの接続、設定ファイルの編集)ができること
- ネットワークの基礎知識があること
レプリケーションとは
レプリケーション(Replication)とは、データベースの内容を複数のサーバー間で複製・同期する仕組みです。レプリケーションを構成することで、以下のメリットを得られます。
| メリット | 説明 |
|---|---|
| 高可用性(HA) | プライマリサーバー障害時にスタンバイへ切り替えてサービス継続 |
| 負荷分散 | 読み取りクエリをスタンバイサーバーに分散 |
| データ保護 | 複数拠点にデータを保持し災害対策 |
| メンテナンス | ローリングアップデートによる無停止メンテナンス |
PostgreSQLにおけるレプリケーションの種類
PostgreSQLでは複数のレプリケーション方式が提供されています。
flowchart TD
A[PostgreSQL レプリケーション] --> B[物理レプリケーション]
A --> C[論理レプリケーション]
B --> D[ファイルベース<br/>ログシッピング]
B --> E[ストリーミング<br/>レプリケーション]
C --> F[パブリケーション/<br/>サブスクリプション]| 方式 | 概要 | ユースケース |
|---|---|---|
| ファイルベースログシッピング | WALファイル単位で転送 | シンプルな災害対策 |
| ストリーミングレプリケーション | WALレコードをリアルタイム転送 | 高可用性構成 |
| 論理レプリケーション | 論理的な変更をパブリッシュ/サブスクライブ | 部分的なデータ同期、異バージョン間移行 |
本記事では、最も一般的に使用されるストリーミングレプリケーションに焦点を当てて解説します。
プライマリとスタンバイの役割
レプリケーション構成における各サーバーの役割を整理します。
プライマリサーバー(Primary Server)
書き込み・読み取りの両方を処理するメインサーバーです。すべてのデータ変更はプライマリで行われ、その変更がスタンバイに伝播されます。
スタンバイサーバー(Standby Server)
プライマリからWAL(Write-Ahead Log)を受信し、継続的に適用するサーバーです。スタンバイは以下の2種類に分類されます。
| 種類 | 説明 | 読み取りクエリ |
|---|---|---|
| ウォームスタンバイ | 昇格するまで接続を受け付けない | 不可 |
| ホットスタンバイ | 読み取り専用クエリを受け付ける | 可能 |
現在のPostgreSQLでは、ホットスタンバイがデフォルトで有効になっており、スタンバイサーバーを読み取り専用のレプリカとして活用できます。
ストリーミングレプリケーションの仕組み
ストリーミングレプリケーションは、プライマリサーバーで生成されたWALレコードを、ファイル単位ではなくレコード単位でスタンバイに転送する方式です。
WAL(Write-Ahead Log)とは
WALは、データベースへの変更を永続化する前にログファイルに書き出す仕組みです。PostgreSQLでは、すべてのデータ変更がまずWALに記録され、その後実際のデータファイルに反映されます。
sequenceDiagram
participant Client as クライアント
participant Primary as プライマリ
participant WAL as WALバッファ
participant Disk as ディスク
participant Standby as スタンバイ
Client->>Primary: INSERT/UPDATE/DELETE
Primary->>WAL: WALレコード書き込み
WAL->>Disk: WAL永続化
Primary-->>Client: COMMIT応答
WAL->>Standby: WALレコード転送
Standby->>Standby: WAL適用ストリーミングレプリケーションの通信
ストリーミングレプリケーションでは、スタンバイサーバーがプライマリサーバーに対してTCP接続を確立し、WALレコードをリアルタイムで受信します。
プライマリ側ではwalsenderプロセスが、スタンバイ側ではwalreceiverプロセスが動作し、これらがWALの送受信を担当します。
flowchart LR
subgraph Primary["プライマリサーバー"]
A[PostgreSQL] --> B[walsender]
C[(WALファイル)]
end
subgraph Standby["スタンバイサーバー"]
D[walreceiver] --> E[PostgreSQL]
F[(WALファイル)]
end
B -->|"WALストリーム<br/>(TCP接続)"| Dファイルベースとの比較
ストリーミングレプリケーションとファイルベースログシッピングの違いを整理します。
| 項目 | ファイルベース | ストリーミング |
|---|---|---|
| 転送単位 | WALファイル(16MB) | WALレコード |
| 遅延 | ファイルが満たされるまで待機 | ほぼリアルタイム(通常1秒未満) |
| データ損失リスク | 最大1ファイル分の損失可能性 | 最小限 |
| 設定の複雑さ | シンプル | やや複雑 |
| ネットワーク要件 | 断続的な接続で可 | 常時接続が必要 |
ストリーミングレプリケーションは、ファイルベースと組み合わせて使用することも可能です。ファイルベースをフォールバックとして設定しておくことで、ネットワーク断時にもWALアーカイブからリカバリできます。
プライマリ・スタンバイ構成の構築
実際にストリーミングレプリケーション環境を構築する手順を解説します。
環境の前提
本記事では以下の構成を想定します。
| サーバー | ホスト名 | IPアドレス | 役割 |
|---|---|---|---|
| プライマリ | primary | 192.168.1.10 | 書き込み/読み取り |
| スタンバイ | standby | 192.168.1.20 | 読み取り専用 |
PostgreSQL 17を使用し、両サーバーにPostgreSQLがインストール済みの状態から始めます。
プライマリサーバーの設定
レプリケーション用ユーザーの作成
まず、スタンバイサーバーがプライマリに接続するための専用ユーザーを作成します。
|
|
REPLICATION権限は、WALストリームを読み取るために必要な特別な権限です。セキュリティの観点から、スーパーユーザーではなく専用のレプリケーションユーザーを使用することを推奨します。
postgresql.confの設定
プライマリサーバーのpostgresql.confを編集し、レプリケーションに必要な設定を行います。
|
|
以下の設定を追加・変更します。
# 接続設定
listen_addresses = '*' # すべてのインターフェースでリッスン
# WAL設定
wal_level = replica # レプリケーションに必要なWALレベル
max_wal_senders = 5 # 同時接続可能なwalsenderプロセス数
wal_keep_size = 1GB # 保持するWALサイズ(スタンバイ切断時のバッファ)
# レプリケーションスロット(推奨)
max_replication_slots = 5 # レプリケーションスロットの最大数
# アーカイブ設定(オプション、推奨)
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/wal_archive/%f'
各パラメータの意味を解説します。
| パラメータ | 説明 |
|---|---|
wal_level |
replica以上でレプリケーションに必要な情報がWALに記録される |
max_wal_senders |
同時に接続可能なスタンバイの数 + バックアップ用に余裕を持たせる |
wal_keep_size |
スタンバイ切断時に保持するWALサイズ。再接続時のキャッチアップに使用 |
max_replication_slots |
レプリケーションスロットを使用する場合に必要 |
pg_hba.confの設定
スタンバイサーバーからのレプリケーション接続を許可します。
# pg_hba.confに追加
# TYPE DATABASE USER ADDRESS METHOD
host replication replicator 192.168.1.20/32 scram-sha-256
DATABASEフィールドにreplicationを指定することで、レプリケーション接続専用のルールとなります。
設定を反映するためにPostgreSQLを再起動します。
|
|
レプリケーションスロットの作成
レプリケーションスロットは、スタンバイが必要とするWALセグメントをプライマリが自動的に保持する仕組みです。スタンバイが長時間切断されても、必要なWALが削除されることを防ぎます。
|
|
slot_name | lsn
--------------+-----
standby_slot |
(1 row)
スタンバイサーバーの設定
ベースバックアップの取得
スタンバイサーバーを初期化するために、pg_basebackupを使用してプライマリのベースバックアップを取得します。
まず、スタンバイサーバーの既存データディレクトリをクリアします(新規構築の場合)。
|
|
ベースバックアップを取得します。
|
|
各オプションの意味は以下の通りです。
| オプション | 説明 |
|---|---|
-h |
プライマリサーバーのホスト名/IP |
-D |
バックアップ先ディレクトリ |
-U |
レプリケーションユーザー |
-P |
進捗表示 |
-R |
standby.signalファイルと接続情報を自動作成 |
-X stream |
バックアップ中のWALもストリーミングで取得 |
-S |
使用するレプリケーションスロット名 |
-Rオプションを指定することで、以下のファイルが自動的に作成されます。
standby.signal: スタンバイモードで起動することを示すファイルpostgresql.auto.conf:primary_conninfoなどの接続設定
postgresql.auto.confの確認
-Rオプションで生成された設定を確認します。
|
|
# 自動生成される内容
primary_conninfo = 'user=replicator password=your_secure_password host=192.168.1.10 port=5432 sslmode=prefer'
primary_slot_name = 'standby_slot'
必要に応じてpostgresql.confに追加設定を行います。
# ホットスタンバイを有効化(読み取りクエリを受け付ける)
hot_standby = on
スタンバイサーバーの起動
|
|
レプリケーション状態の確認
レプリケーションが正常に動作しているか確認します。
プライマリ側での確認
|
|
出力例:
pid | usename | application_name | client_addr | state | sync_state | sent_lsn | write_lsn | flush_lsn | replay_lsn
-------+------------+------------------+---------------+-----------+------------+---------------+---------------+---------------+---------------
12345 | replicator | walreceiver | 192.168.1.20 | streaming | async | 0/5000060 | 0/5000060 | 0/5000060 | 0/5000060
各フィールドの意味:
| フィールド | 説明 |
|---|---|
state |
streamingなら正常に動作中 |
sync_state |
async(非同期)またはsync(同期) |
sent_lsn |
送信済みのWAL位置 |
replay_lsn |
スタンバイで適用済みのWAL位置 |
スタンバイ側での確認
|
|
is_standby | receive_lsn | replay_lsn | last_replay_time
------------+---------------+---------------+-------------------------------
t | 0/5000060 | 0/5000060 | 2026-01-03 18:30:45.123456+09
pg_is_in_recovery()がtrueを返せば、スタンバイモードで動作しています。
レプリケーション遅延の確認
|
|
同期・非同期レプリケーション
ストリーミングレプリケーションには、同期モードと非同期モードの2種類があります。それぞれの特徴を理解し、要件に応じて使い分けることが重要です。
非同期レプリケーション
デフォルトの動作モードです。プライマリはトランザクションをコミットした後、スタンバイへのWAL転送完了を待たずにクライアントに応答を返します。
sequenceDiagram
participant C as クライアント
participant P as プライマリ
participant S as スタンバイ
C->>P: COMMIT
P->>P: WAL書き込み
P-->>C: COMMIT完了
P->>S: WAL転送(非同期)
S->>S: WAL適用メリット
- 低レイテンシ: スタンバイの応答を待たないため高速
- スタンバイ障害に強い: スタンバイが停止してもプライマリの性能に影響しない
デメリット
- データ損失の可能性: プライマリ障害時、未転送のWALが失われる可能性がある
同期レプリケーション
プライマリがクライアントにCOMMIT応答を返す前に、少なくとも1台の同期スタンバイからWAL受信の確認を待ちます。
sequenceDiagram
participant C as クライアント
participant P as プライマリ
participant S as スタンバイ
C->>P: COMMIT
P->>P: WAL書き込み
P->>S: WAL転送
S->>S: WAL書き込み
S-->>P: ACK(確認応答)
P-->>C: COMMIT完了
S->>S: WAL適用メリット
- データ損失ゼロ: コミット済みのトランザクションは必ずスタンバイにも存在する
- 高い耐久性: プライマリとスタンバイの同時障害でない限りデータを保護
デメリット
- レイテンシ増加: スタンバイの応答を待つため、ネットワーク遅延が性能に直結
- 可用性への影響: 同期スタンバイがすべて停止するとプライマリのコミットもブロック
同期レプリケーションの設定
プライマリサーバーのpostgresql.confでsynchronous_standby_namesを設定します。
# スタンバイのapplication_nameを指定
synchronous_standby_names = 'standby1'
スタンバイ側ではprimary_conninfoにapplication_nameを追加します。
primary_conninfo = 'user=replicator password=xxx host=192.168.1.10 port=5432 application_name=standby1'
synchronous_commitの設定
synchronous_commitパラメータで、同期レプリケーションの強度を調整できます。
| 設定値 | 動作 | データ損失リスク |
|---|---|---|
off |
WAL書き込みを待たない | 高(プライマリクラッシュで損失) |
local |
ローカルWAL書き込みのみ待機 | 中(プライマリ障害時に損失) |
remote_write |
スタンバイのOS層での受信を待機 | 低(スタンバイOSクラッシュで損失) |
on |
スタンバイのディスク書き込みを待機 | 最小(同時障害でのみ損失) |
remote_apply |
スタンバイでのWAL適用完了を待機 | 最小 + 即座に読み取り可能 |
トランザクション単位で変更することも可能です。
|
|
複数スタンバイの同期設定
複数のスタンバイサーバーがある場合、優先順位ベースまたはクォーラムベースで同期スタンバイを指定できます。
優先順位ベース(FIRST)
# s1, s2のうち最初の2台を同期スタンバイとして使用
synchronous_standby_names = 'FIRST 2 (s1, s2, s3)'
クォーラムベース(ANY)
# s1, s2, s3のうち任意の2台からの確認を待機
synchronous_standby_names = 'ANY 2 (s1, s2, s3)'
クォーラムベースでは、特定のスタンバイに依存せず、指定した数のスタンバイから確認が得られればコミットが完了します。地理的に分散したスタンバイ構成で有効です。
フェイルオーバーの考え方
フェイルオーバーとは、プライマリサーバーに障害が発生した際にスタンバイサーバーを新しいプライマリに昇格させ、サービスを継続する操作です。
フェイルオーバーの基本フロー
flowchart TD
A[プライマリ障害発生] --> B{障害検知}
B --> C[スタンバイを昇格]
C --> D[アプリケーション接続先変更]
D --> E[サービス継続]
E --> F[旧プライマリの処理]
F --> G{復旧可能?}
G -->|Yes| H[スタンバイとして再構築]
G -->|No| I[新規スタンバイ構築]スタンバイの昇格方法
PostgreSQLでは、以下の方法でスタンバイをプライマリに昇格できます。
pg_ctl promoteコマンド
|
|
pg_promote()関数
|
|
昇格が完了すると、standby.signalファイルが削除され、サーバーは読み書き可能なプライマリとして動作を開始します。
昇格後の確認
|
|
スプリットブレインの防止
フェイルオーバーで最も注意すべき問題が「スプリットブレイン」です。これは、旧プライマリと新プライマリの両方が同時に書き込みを受け付けてしまう状態を指します。
flowchart LR
subgraph Problem["スプリットブレイン状態"]
A[旧プライマリ] -->|"書き込み"| C[(データA)]
B[新プライマリ] -->|"書き込み"| D[(データB)]
end
E[データ不整合発生]
C --> E
D --> Eスプリットブレインを防ぐための対策を紹介します。
STONITH(Shoot The Other Node In The Head)
旧プライマリを確実に停止させる仕組みです。フェンシング(fencing)とも呼ばれます。
- 電源管理ユニット(PDU)による強制電源断
- IPMIによるリモートシャットダウン
- ストレージフェンシング
自動フェイルオーバーツールの使用
本番環境では、手動フェイルオーバーではなく自動フェイルオーバーツールの使用を推奨します。
| ツール | 特徴 |
|---|---|
| Patroni | etcd/Consul/ZooKeeperを使用した分散合意ベース |
| repmgr | PostgreSQL専用のレプリケーション管理ツール |
| Pgpool-II | コネクションプーリング機能も備えた統合ツール |
| pg_auto_failover | Citus製のシンプルな自動フェイルオーバー |
旧プライマリの再統合
フェイルオーバー後、旧プライマリを新しいスタンバイとして再構築する方法は2つあります。
pg_basebackupによる再構築
新プライマリからベースバックアップを取得し、スタンバイとして構築し直します。確実ですが、データ量が多いと時間がかかります。
pg_rewindによる高速再同期
pg_rewindは、旧プライマリと新プライマリの差分を巻き戻し、旧プライマリをスタンバイとして再接続可能にするツールです。
|
|
pg_rewindを使用するには、以下の条件が必要です。
wal_log_hints = onまたはdata_checksumsが有効- 旧プライマリで未適用のWALが新プライマリに存在する
レプリケーション構成のベストプラクティス
レプリケーションスロットの活用
レプリケーションスロットを使用することで、スタンバイが必要とするWALが自動的に保持されます。ただし、スタンバイが長期間切断されると、WALが蓄積しディスクを圧迫する可能性があります。
# WAL蓄積の上限を設定
max_slot_wal_keep_size = 10GB
監視項目
レプリケーション環境では、以下の項目を継続的に監視することを推奨します。
| 監視項目 | 確認方法 | 警告しきい値(例) |
|---|---|---|
| レプリケーション遅延 | pg_stat_replication |
1MB以上 |
| スタンバイ接続状態 | pg_stat_replication.state |
streaming以外 |
| WALディスク使用量 | pg_walディレクトリサイズ |
80%以上 |
| レプリケーションスロット状態 | pg_replication_slots |
inactive |
ネットワーク設計
レプリケーション用のネットワークは、アプリケーション通信と分離することを推奨します。
flowchart TB
subgraph Network["ネットワーク構成"]
subgraph Primary["プライマリ"]
P1[eth0: アプリ用<br/>192.168.1.10]
P2[eth1: レプリ用<br/>10.0.0.10]
end
subgraph Standby["スタンバイ"]
S1[eth0: アプリ用<br/>192.168.1.20]
S2[eth1: レプリ用<br/>10.0.0.20]
end
end
P2 <-->|"レプリケーション<br/>(専用セグメント)"| S2まとめ
本記事では、PostgreSQLのストリーミングレプリケーションを中心に、高可用性構成の基礎を解説しました。
学習した内容を振り返ります。
| トピック | ポイント |
|---|---|
| レプリケーションの種類 | 物理(ストリーミング)と論理の違いを理解する |
| ストリーミングレプリケーション | WALレコード単位のリアルタイム転送で遅延を最小化 |
| 構築手順 | レプリケーションユーザー作成、設定変更、pg_basebackupで初期化 |
| 同期・非同期 | データ損失リスクとレイテンシのトレードオフ |
| フェイルオーバー | pg_ctl promote/pg_promote()で昇格、スプリットブレイン対策が重要 |
本番環境での運用においては、本記事で紹介した手動フェイルオーバーではなく、Patroniなどの自動フェイルオーバーツールの導入を検討してください。また、定期的なフェイルオーバー訓練を実施し、実際の障害時に確実に対応できる体制を整えることが重要です。
参考リンク
- PostgreSQL Documentation: High Availability, Load Balancing, and Replication
- PostgreSQL Documentation: Log-Shipping Standby Servers
- PostgreSQL Documentation: Streaming Replication
- PostgreSQL Documentation: Synchronous Replication
- PostgreSQL Documentation: Failover
- pg_basebackup Documentation
- pg_rewind Documentation
- Patroni Documentation