インフラエンジニアの羅針盤

インフラエンジニア1〜3年目のための技術ガイド

PostgreSQL 18 冗長化入門:ストリーミングレプリケーション構築

公開
現場で迷わない PostgreSQL 18 構築・運用ガイド 第9回 アイキャッチ:ストリーミングレプリケーション構築手順



データベースを単一サーバー(シングルノード)で運用している場合、ハードウェア故障や電源障害、OSパッチ適用に伴う再起動などのイベントが発生すると、システム全体のサービス停止(ダウンタイム)に直結します。この単一障害点(SPOF: Single Point of Failure)を排除し、システムの可用性と耐障害性を高めるための基礎技術が、PostgreSQL標準の機能であるStreaming Replication(ストリーミング・レプリケーション)です。

レプリケーションを導入することで、本番データを常時スタンバイサーバーへリアルタイム複製できるだけでなく、読み取りクエリをスタンバイへ逃がす「参照負荷分散(リードレプリカ運用)」も実現可能となります。

本記事の技術的到達目標

  • Streaming Replication の内部メカニズム(walsenderwalreceiverstartup プロセスの協調動作)を理解する。
  • 非同期レプリケーション(Async)と同期レプリケーション(Sync)の設計トレードオフを把握する。
  • pg_basebackup -R が自動生成する設定(standby.signalprimary_conninfo)の役割と、PostgreSQL 12以降の現行仕様を整理する。
  • 実機2台(AlmaLinux 10 / alma-db01 および alma-db02)を用いて、初期複製からサービス起動、pg_stat_replication による遅延監視、リードレプリカ保護の実証、手動昇格(Promote)までの一連の手順を習得する。

本記事では、冗長化の第一歩となるプライマリ・スタンバイ2台構成の構築手順と、実務で不可欠となる運用監視クエリを体系的に解説します。

目次
  1. 1. Streaming Replication のアーキテクチャと動作原理
    1. 1.1 プライマリとスタンバイの役割分担
    2. 1.2 WAL転送の内部プロセス
    3. 1.3 非同期レプリケーション vs 同期レプリケーション
  2. 2. 構築前のネットワーク・認証・パラメータ設計
    1. 2.1 レプリケーション専用ロールの作成
    2. 2.2 pg_hba.conf での replication 接続許可
    3. 2.3 postgresql.conf の必須パラメータ
  3. 3. pg_basebackup -R による初期スタンバイ生成の内部動作
    1. 3.1 物理クラスタ複製の仕組み
    2. 3.2 -R オプションが自動生成するもの
    3. 3.3 -c fast(チェックポイント即時完了)の必須性
  4. 4. 実務標準:Streaming Replication 構築の6ステップワークフロー
  5. 5. 【実機検証】AlmaLinux 10での Streaming Replication 構築実践
    1. 5.1 検証環境構成
    2. 5.2 プライマリ側の設定と初期データ作成
    3. 5.3 スタンバイ側での pg_basebackup -R 実行
    4. 5.4 スタンバイ起動と接続ステータス確認
    5. 5.5 プライマリ側ビューによる同期状態と遅延の監視
    6. 5.5.1 レプリケーションスロットの健全性監視(pg_replication_slots)
    7. 5.5.2 同期レプリケーション(synchronous_commit)の設計基準
    8. 5.6 同期整合性とリードレプリカ保護の実証
  6. 6. 実務チートシート:レプリケーション遅延監視クエリとトラブルシューティング
    1. 6.1 詳細遅延監視クエリ(送信・ディスク書込・フラッシュ・再生)
    2. 6.2 よくあるトラブルと対処マトリクス
  7. 7. 理解度確認演習問題(全3問・解答解説付き)
  8. 8. まとめとシリーズ完結

1. Streaming Replication のアーキテクチャと動作原理

PostgreSQLのStreaming Replicationは、データベースの更新履歴であるWAL(Write-Ahead Logging: 先行書き込みログ)をネットワーク経由でリアルタイム転送し、スタンバイ側で連続的に再生(リプレイ)する仕組みです。

PostgreSQL Streaming Replication の内部アーキテクチャとプロセス間通信
図1: PostgreSQL Streaming Replication の内部アーキテクチャとプロセス間通信

1.1 プライマリとスタンバイの役割分担

標準的なStreaming Replication構成における各ノードの役割は明確に分離されています。

  • プライマリサーバー(Primary / 主系):読み取り(SELECT)および書き込み(INSERT / UPDATE / DELETE)の双方を受け付けます。データの変更が発生するたびにWALレコードを生成し、ローカルディスクへコミットすると同時にスタンバイへ送出します。
  • スタンバイサーバー(Standby / 従系 / リードレプリカ):プライマリから送られてくるWALを順次適用し、プライマリと同一のデータ状態を維持します。通常時は読み取り専用(ReadOnly)として動作し、参照クエリの負荷分散を担います。スタンバイに対して直接更新クエリを実行することはできません。

1.2 WAL転送の内部プロセス

レプリケーションの通信は、プライマリ側とスタンバイ側の専用バックグラウンドプロセスによって制御されます。

プロセス名 稼働ノード 主な役割と動作
walsender プライマリ スタンバイからのTCP接続を受け付け、ローカルのWALディスク(pg_wal)から差分レコードを読み出してネットワーク経由でストリーミング送信する。またスタンバイからの受信・再生進捗応答を受け取る。
walreceiver スタンバイ プライマリの walsender へ接続を確立し、送られてきたWALストリームを受信してスタンバイ側の pg_wal 領域へディスク書き込みを行う。受信状況をプライマリへフィードバックする。
startup スタンバイ walreceiver が書き込んだWALファイルを順次読み出し、ローカルのデータブロックへ更新を適用(リカバリ再生)する。

このパイプライン構造により、プライマリでコミットされたトランザクションは通常数ミリ秒以内の極めて微小な遅延でスタンバイ側に反映されます。

1.3 非同期レプリケーション vs 同期レプリケーション

Streaming Replicationには「非同期(Asynchronous)」と「同期(Synchronous)」の2つの動作モードが存在します。

  • 非同期レプリケーション(デフォルト・実務推奨):プライマリはローカルディスクへのWAL書き込みが完了した時点でクライアントへトランザクション完了(COMMIT)を応答します。スタンバイへの転送完了を待機しないため、ネットワーク遅延の影響を受けず、プライマリの書き込み性能が最大化されます。万一のプライマリ障害時には、未転送のトランザクションがわずかに失われる可能性(RPO > 0)があります。
  • 同期レプリケーション(synchronous_commit有効時):プライマリはスタンバイ側でのWAL受信・ディスク書き込み完了通知を受け取るまでクライアントへのコミット応答を保留します。プライマリが突発停止してもデータ喪失がゼロ(RPO = 0)に抑えられますが、ネットワークラウンドトリップ時間の分だけトランザクション応答時間が長くなります。

Webサービスや社内情報システム、一般的な業務アプリケーションでは、パフォーマンスと耐障害性のバランスから非同期レプリケーションが標準的に採用されます。

2. 構築前のネットワーク・認証・パラメータ設計

Streaming Replicationを構築するにあたり、プライマリ側で事前に施すべき3つの設計ポイントが存在します。

2.1 レプリケーション専用ロールの作成

プライマリへのレプリケーション接続には、通常のデータベースアクセス権限ではなく、専用の REPLICATION 属性を持つロールを使用します。

-- プライマリ側で実行
CREATE ROLE repuser WITH REPLICATION LOGIN ENCRYPTED PASSWORD 'rep_secret_pass';

SUPERUSER 権限を付与することなく、レプリケーション通信に必要な最小限の権限のみを付与することで、セキュリティを担保します。

2.2 pg_hba.conf での replication 接続許可

PostgreSQLでは、通常のデータベース(all)とレプリケーション通信(replication)の認証ルールが分離されています。スタンバイのIPアドレスからの replication 疑似データベースへのアクセスを許可します。

# pg_hba.conf への追記設定例
# TYPE  DATABASE        USER     ADDRESS             METHOD
host    replication     repuser  192.168.2.135/32    scram-sha-256

接続元をスタンバイのIPアドレス(/32)に厳密に絞り込むことが実務上のセキュリティ原則です。

2.3 postgresql.conf の必須パラメータ

PostgreSQL 18において、レプリケーションに関わる基本パラメータを確認します。

パラメータ名 推奨設定値 設定の意義
listen_addresses '*' スタンバイからのTCP接続(ポート5432)を受け付けるために必須。
wal_level replica WALに必要な差分情報を含める設定。PostgreSQL 10以降はデフォルトで replica のため変更不要。
max_wal_senders 10(デフォルト) 同時接続可能な walsender の上限数。スタンバイ台数以上の数値を確保。
wal_keep_size 1GB5GB スタンバイが一時切断された際、プライマリがディスク上に保持するWALの最小容量。本番運用では物理レプリケーションスロット(replication slot)によるWAL保持を基本とし、スロット未接続時や初期構築時のフェイルセーフとして併用。

3. pg_basebackup -R による初期スタンバイ生成の内部動作

スタンバイサーバーを立ち上げるためには、プライマリサーバーのデータディレクトリ(データブロックおよびWALログ)の完全な物理コピーを初期配置する必要があります。この処理を一括実行するツールが pg_basebackup です。

3.1 物理クラスタ複製の仕組み

pg_basebackup は、プライマリのストレージ領域を直接ファイルコピーするのではなく、PostgreSQLのレプリケーションプロトコル経由でプライマリからデータブロック群をストリーミング受信します。データベースのオンライン稼働中であっても、整合性を保ったベースバックアップを採取できます。

3.2 -R オプションが自動生成するもの

pg_basebackup の実行時に -R(または --write-recovery-conf)オプションを指定すると、単なるバックアップに留まらず、スタンバイとして起動するための設定ファイルを自動生成します。

  1. standby.signal ファイルの生成:
    データディレクトリ直下に0バイトの空ファイル standby.signal を作成します。PostgreSQLサーバーはこのファイルが存在する場合、通常モードではなく「スタンバイ(リカバリ)モード」として起動します。
  2. postgresql.auto.conf への primary_conninfo 追記:
    pg_basebackup 実行時に使用した接続情報(ホスト名、ポート、ユーザー名、パスワード等)を元に、プライマリへの接続文字列を primary_conninfo パラメータとして設定ファイルへ自動追記します。

知見:PostgreSQL 12以降のリカバリ設定統一

古い技術記事や教本では「スタンバイ構築時に recovery.conf ファイルを作成する」という手順が多数存在しますが、recovery.confPostgreSQL 12 で完全に廃止 されました。

現行バージョン(PostgreSQL 12〜18)では、standby.signal の配置と、通常の設定ファイル(postgresql.conf または postgresql.auto.conf)内での primary_conninfo 定義に完全統一されています。pg_basebackup -R を使用すれば、この現行仕様に適合した設定が自動構築されます。

3.3 -c fast(チェックポイント即時完了)の必須性

実務で pg_basebackup を実行する際、必ず付与すべきオプションが -c fast--checkpoint=fast)です。

デフォルト動作では、プライマリ側のI/Oスパイクを避けるために通常のチェックポイント完了を待機するため、バックアップ開始までに数十秒から数分間処理がスタックします。-c fast を指定することで、プライマリに対して即時チェックポイントの実行を指示し、待機時間ゼロでバックアップ処理を開始できます。

4. 実務標準:Streaming Replication 構築の6ステップワークフロー

2台のサーバーを用いてレプリケーション環境を無駄なく構築するための標準手順を提示します。

Streaming Replication 構築から稼働監視までの6ステップ標準ワークフロー
図2: Streaming Replication 構築から稼働監視までの6ステップ標準ワークフロー

Streaming Replication 構築手順

  1. プライマリ側 認証・権限設計:レプリケーション専用ロール(repuser)を作成し、pg_hba.conf にスタンバイIPからの接続許可を追記します。
  2. プライマリ側 パラメータ反映:listen_addresses = '*' を確認し、ファイアウォール(5432/tcp)を開放した上で設定をリロードまたは再起動します。
  3. スタンバイ側 初期ベースバックアップ:スタンバイ側の空データディレクトリに対して pg_basebackup -R -c fast を実行し、クラスタを複製します。
  4. スタンバイ側 サービス起動:スタンバイの postgresql-18 を起動し、ログからリカバリモード突入とWALストリーミング開始を確認します。
  5. プライマリ側 同期監視・遅延算出:プライマリ側の pg_stat_replication ビューを照合し、state: streaming および遅延バイト数が正常範囲内であることを確認します。
  6. 同期整合性&読み取り専用保護の実証:プライマリでのINSERTがスタンバイへ即座に反映されること、およびスタンバイでの書き込みがシステム的に拒絶されることを確認します。

5. 【実機検証】AlmaLinux 10での Streaming Replication 構築実践

実機検証環境の2台(プライマリ: alma-db01 / スタンバイ: alma-db02)を用いて、実際に非同期Streaming Replicationを構築したログと検証結果を提示します。

5.1 検証環境構成

  • プライマリ(Primary):alma-db01(IP: 192.168.2.132 / AlmaLinux 10.2 / PostgreSQL 18.6)
  • スタンバイ(Standby):alma-db02(IP: 192.168.2.135 / AlmaLinux 10.2 / PostgreSQL 18.6)

5.2 プライマリ側の設定と初期データ作成

プライマリ側で初期設定とテストテーブルの作成を行います。

# 1. 外部接続と認証ルールの設定
$ sudo sed -i "s/#listen_addresses = 'localhost'/listen_addresses = '*'/g" /var/lib/pgsql/18/data/postgresql.conf
$ echo "host replication repuser 192.168.2.135/32 scram-sha-256" | sudo tee -a /var/lib/pgsql/18/data/pg_hba.conf
$ echo "host all all 192.168.2.0/24 scram-sha-256" | sudo tee -a /var/lib/pgsql/18/data/pg_hba.conf

# 2. ファイアウォール開放とサービス起動
$ sudo firewall-cmd --add-service=postgresql --permanent && sudo firewall-cmd --reload
$ sudo systemctl enable --now postgresql-18

# 3. レプリケーションロールの作成とテストデータ投入
$ sudo -u postgres /usr/pgsql-18/bin/psql -c "CREATE ROLE repuser WITH REPLICATION LOGIN ENCRYPTED PASSWORD 'rep_secret_pass';"
$ sudo -u postgres /usr/pgsql-18/bin/psql -c "CREATE DATABASE production_db;"
$ sudo -u postgres /usr/pgsql-18/bin/psql -d production_db -c "
CREATE TABLE public.cluster_nodes (
    node_id serial PRIMARY KEY,
    hostname text NOT NULL,
    role text NOT NULL,
    ip_address inet NOT NULL,
    registered_at timestamptz DEFAULT current_timestamp
);
INSERT INTO public.cluster_nodes (hostname, role, ip_address) VALUES
    ('alma-db01', 'Primary', '192.168.2.132'),
    ('alma-db02', 'Standby', '192.168.2.135');
"

# 4. 物理レプリケーションスロットの事前作成(プライマリ側)
$ sudo -u postgres /usr/pgsql-18/bin/psql -c "SELECT pg_create_physical_replication_slot('standby_slot_01');"
 pg_create_physical_replication_slot 
-------------------------------------
 (standby_slot_01,)
(1 行)

5.3 スタンバイ側での pg_basebackup -R 実行

スタンバイ(alma-db02)にログインし、空のデータディレクトリに対して事前作成したレプリケーションスロット(standby_slot_01)を指定してベースバックアップを実行します。

$ sudo -u postgres PGPASSWORD='rep_secret_pass' /usr/pgsql-18/bin/pg_basebackup \
  -h 192.168.2.132 \
  -p 5432 \
  -U repuser \
  -D /var/lib/pgsql/18/data \
  -Fp \
  -Xs \
  -c fast \
  -S standby_slot_01 \
  -P \
  -R -v

実行ログ(実測結果):

pg_basebackup: ベースバックアップを開始しています - チェックポイントの完了を待機中
pg_basebackup: チェックポイントが完了しました
pg_basebackup: 先行書き込みログの開始ポイント: タイムライン 1 上の 0/3000028
pg_basebackup: バックグランドWAL受信処理を起動します
pg_basebackup: レプリケーションスロット"standby_slot_01"を使用しています
31386/31386 kB (100%), 0/1 テーブル空間 (.../pgsql/18/data/global/pg_control)
31386/31386 kB (100%), 1/1 テーブル空間                                         
pg_basebackup: 先行書き込みログの終了ポイント: 0/3000120
pg_basebackup: バックグランドプロセスがストリーミング処理が終わるまで待機します ...
pg_basebackup: データをディスクに同期しています...
pg_basebackup: backup_manifest.tmp の名前を backup_manifest に変更しています
pg_basebackup: ベースバックアップが完了しました

所要時間はわずか1秒でした。生成されたファイルを確認します。

# standby.signal の存在確認(サイズ0バイト)
$ sudo ls -la /var/lib/pgsql/18/data/standby.signal
-rw-------. 1 postgres postgres 0  9月  9 23:02 /var/lib/pgsql/18/data/standby.signal

# postgresql.auto.conf に追記された接続情報とスロット設定
$ sudo cat /var/lib/pgsql/18/data/postgresql.auto.conf
# Do not edit this file manually!
# It will be overwritten by the ALTER SYSTEM command.
primary_conninfo = 'user=repuser password=rep_secret_pass host=192.168.2.132 port=5432 ...'
primary_slotname = 'standby_slot_01'

5.4 スタンバイ起動と接続ステータス確認

スタンバイのPostgreSQLサービスを起動し、ログを確認します。

$ sudo firewall-cmd --add-service=postgresql --permanent && sudo firewall-cmd --reload
$ sudo systemctl enable --now postgresql-18

スタンバイ起動ログ(/var/lib/pgsql/18/data/log/postgresql-Wed.log):

LOG:  スタンバイモードに入ります
LOG:  REDOを0/3000028から開始します
LOG:  0/3000120 でリカバリの一貫性が確保されました
LOG:  データベースシステムはリードオンリー接続の受け付け準備ができました
LOG:  プライマリのタイムライン1の 0/4000000からでWALストリーミングを始めます

スタンバイ側で関数を実行し、リカバリ中であることを確認します。

$ sudo -u postgres /usr/pgsql-18/bin/psql -c "SELECT pg_is_in_recovery();"
 pg_is_in_recovery 
-------------------
 t
(1 行)

$ sudo -u postgres /usr/pgsql-18/bin/psql -x -c "SELECT pid, status, receive_start_lsn, written_lsn, flushed_lsn, sender_host, sender_port FROM pg_stat_wal_receiver;"
-[ RECORD 1 ]-----+------------
pid               | 6818
status            | streaming
receive_start_lsn | 0/4000000
written_lsn       | 0/404DFA0
flushed_lsn       | 0/404DFA0
sender_host       | 192.168.2.132
sender_port       | 5432

5.5 プライマリ側ビューによる同期状態と遅延の監視

プライマリ(alma-db01)からレプリケーションビュー pg_stat_replication を照合します。

$ sudo -u postgres /usr/pgsql-18/bin/psql -x -c "SELECT pid, usename, application_name, client_addr, state, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn FROM pg_stat_replication;"
-[ RECORD 1 ]----+--------------
pid              | 3723
usename          | repuser
application_name | walreceiver
client_addr      | 192.168.2.135
state            | streaming
sync_state       | async
sent_lsn         | 0/404DFA0
write_lsn        | 0/404DFA0
flush_lsn        | 0/404DFA0
replay_lsn       | 0/404DFA0

レプリケーション遅延(Replication Lag)をバイト数で算出するクエリを実行します。

$ sudo -u postgres /usr/pgsql-18/bin/psql -c "
SELECT
    client_addr,
    application_name,
    state,
    sync_state,
    pg_current_wal_lsn() AS current_wal_lsn,
    replay_lsn,
    pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replication_lag_bytes
FROM pg_stat_replication;
"
  client_addr  | application_name |   state   | sync_state | current_wal_lsn | replay_lsn | replication_lag_bytes 
---------------+------------------+-----------+------------+-----------------+------------+-----------------------
 192.168.2.135 | walreceiver      | streaming | async      | 0/404DFA0       | 0/404DFA0  |                     0
(1 行)

replication_lag_bytes0 であり、完全同期を維持していることが確認できました。

5.5.1 レプリケーションスロットの健全性監視(pg_replication_slots)

ストリーミングレプリケーション運用において、pg_stat_replication と並んで監視が必須となるのがシステムビュー pg_replication_slots です。

$ sudo -u postgres /usr/pgsql-18/bin/psql -x -c "SELECT slot_name, slot_type, active, restart_lsn, wal_status FROM pg_replication_slots;"
-[ RECORD 1 ]+----------------
slot_name    | standby_slot_01
slot_type    | physical
active       | t
restart_lsn  | 0/404DFA0
wal_status   | reserved

ここで着目すべきは active 列と wal_status 列です。activet(true)であればスタンバイノードが現在接続中であることを示します。もしスタンバイ停止やネットワーク断により activef(false)になった場合、プライマリはスタンバイ復旧に備えてWALセグメントをディスク内に蓄積し続けます。監視システムでは active = f の継続時間、および wal_status = lost(未転送WALが溢れてレプリケーションが破綻した状態)の検知アラートを必ず設定します。

5.5.2 同期レプリケーション(synchronous_commit)の設計基準

本稿で構築した構成は「非同期(async)レプリケーション」です。実務の要件(RPO: 目標復旧時点)に応じて、プライマリの synchronous_commit パラメータを調整することでデータ保護レベルを段階的に強化できます。

synchronous_commit の設定レベルと特性(※synchronous_standby_names設定時)

  • off(非同期・最速):プライマリのローカルディスク書き込み完了を待たずにクライアントへACKを返却。最高のスループットを得られますが、電源障害時に直前トランザクションが失われるリスクがあります。
  • local(ローカル同期・実務標準):プライマリのローカルディスクにWALがflushされた時点でコミット。スタンバイへの転送はバックグラウンドで行われるため、ネットワーク遅延が更新性能に影響しません。
  • on(リモートフラッシュ同期):スタンバイ側のディスクにWALが永続化(flush)されるまでプライマリがコミット完了を待機。プライマリ故障時でもデータ損失(RPO=0)を防止できますが、ネットワーク往復遅延が直接クエリ性能に加算されます。
  • remote_apply(リモート可視化同期):スタンバイ側でWALがリプレイされ、スタンバイへのクエリで変更が参照可能になった時点でコミット。リードレプリカで直前の更新結果が即座に読めない「レプリケーション遅れ(Stale Read)」を防止します。

【最重要】synchronous_standby_names 設定の必須性

synchronous_commitonremote_apply に設定しても、プライマリの postgresql.conf 内で synchronous_standby_names が未設定(空)のままである場合、PostgreSQLは同期対象スタンバイが存在しないと判断し、リモート待機を行わずローカル同期(local相当)として即座にコミットします

完全な同期レプリケーションを成立させるには、以下の通りスタンバイの primary_conninfo に識別名(application_name)を明示し、プライマリ側で監視対象スタンバイ名を指定してリロードします。

# 1. スタンバイ側(/var/lib/pgsql/18/data/postgresql.auto.conf 等)
primary_conninfo = 'host=192.168.2.132 port=5432 user=repuser application_name=standby01 password=...'

# 2. プライマリ側(/var/lib/pgsql/18/data/postgresql.conf)
synchronous_standby_names = 'FIRST 1 (standby01)' # 未設定時はリモート待機が無効化される

# 3. プライマリで設定反映
$ sudo systemctl reload postgresql-18

この設定を行って初めて、プライマリの pg_stat_replication において sync_stateasync から sync へ切り替わり、厳密な同期コミットが保証されます。

5.6 同期整合性とリードレプリカ保護の実証

プライマリにデータを追加し、スタンバイで即座に参照できるかを検証します。

# プライマリ(alma-db01)でデータ追加
$ sudo -u postgres /usr/pgsql-18/bin/psql -d production_db -c "
INSERT INTO public.cluster_nodes (hostname, role, ip_address) VALUES ('alma-db03', 'Standby-2', '192.168.2.136');
"

# スタンバイ(alma-db02)で即座に照会
$ sudo -u postgres /usr/pgsql-18/bin/psql -d production_db -c "SELECT * FROM public.cluster_nodes;"
 node_id | hostname  |   role    |  ip_address   |         registered_at         
---------+-----------+-----------+---------------+-------------------------------
       1 | alma-db01 | Primary   | 192.168.2.132 | 2026-09-09 23:00:48.536817+09
       2 | alma-db02 | Standby   | 192.168.2.135 | 2026-09-09 23:00:48.536817+09
       3 | alma-db03 | Standby-2 | 192.168.2.136 | 2026-09-09 23:03:00.816757+09
(3 行)

プライマリでの追加が即時にスタンバイへ伝播していることが確認できました。次にスタンバイに対して更新クエリを試行します。

# スタンバイ(alma-db02)での書き込み試行
$ sudo -u postgres /usr/pgsql-18/bin/psql -d production_db -c "
INSERT INTO public.cluster_nodes (hostname, role, ip_address) VALUES ('test-fail', 'Invalid', '192.168.2.99');
"
ERROR:  リードオンリーのトランザクションでは INSERT を実行できません

データベースエンジンにより書き込みが厳格に拒絶され、データの不整合が防止されていることを実証できました。

6. 実務チートシート:レプリケーション遅延監視クエリとトラブルシューティング

実務の現場で直面する運用監視とトラブルシューティングのためのチートシートです。

6.1 詳細遅延監視クエリ(送信・ディスク書込・フラッシュ・再生)

レプリケーションの遅延には4段階のフェーズが存在します。それぞれのボトルネックを特定する監視クエリです。

SELECT
    client_addr AS standby_ip,
    application_name,
    state,
    sync_state,
    pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)   AS send_lag_bytes,
    pg_wal_lsn_diff(sent_lsn, write_lsn)              AS write_lag_bytes,
    pg_wal_lsn_diff(write_lsn, flush_lsn)             AS flush_lag_bytes,
    pg_wal_lsn_diff(flush_lsn, replay_lsn)            AS replay_lag_bytes,
    pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS total_lag_bytes,
    replay_lag
FROM pg_stat_replication;
  • send_lag_bytes が大きい場合:ネットワーク帯域不足、またはプライマリの walsender 負荷が高い。
  • flush_lag_bytes が大きい場合:スタンバイ側のディスク書き込みI/O性能が不足。
  • replay_lag_bytes が大きい場合:スタンバイ側で重い参照クエリが実行されており、WAL再生と競合(ロック競合)している。

6.2 よくあるトラブルと対処マトリクス

レプリケーショントラブルシューティング一覧

  • トラブル①:認証エラーで接続拒絶
    症状:FATAL: no pg_hba.conf entry for replication connection from host "192.168.2.135", user "repuser"
    原因と対処:pg_hba.confreplication 用のエントリが不足しているか、IP範囲指定(CIDR)が誤っています。正しく追記して SELECT pg_reload_conf(); を実行します。
  • トラブル②:スタンバイが起動しない / 通常プライマリとして起動してしまう
    症状:pg_is_in_recovery()f を返し、プライマリと同期しない。
    原因と対処:データディレクトリ直下に standby.signal ファイルが存在しないことが原因です。pg_basebackup 時に -R オプションを付け忘れた場合、手動で touch /var/lib/pgsql/18/data/standby.signal を作成して再起動します。
  • トラブル③:WAL枯渇による同期途絶
    症状:FATAL: could not receive data from WAL stream: ERROR: requested WAL segment has already been removed
    原因と対処:スタンバイが長時間停止している間に、プライマリ側で未転送のWALファイルが削除されてしまった状態です。再発防止のためには物理レプリケーションスロット(standby_slot_01)を確実に有効化し、フェイルセーフとして wal_keep_size を併用します。既にセグメントが消失した場合は、スロットを再確認した上で pg_basebackup によるクラスタ再初期化が必要です。

7. 理解度確認演習問題(全3問・解答解説付き)

本記事で解説したStreaming Replicationのアーキテクチャと運用知識を定着させるための確認問題です。

【問1】pg_basebackup の -R オプションが実行する処理として、正しい説明を1つ選択してください。

  1. 旧クラスタのデータをすべて削除して新クラスタへ置き換える。
  2. データディレクトリ直下に recovery.conf ファイルを作成する。
  3. データディレクトリ直下に standby.signal を配置し、postgresql.auto.conf に primary_conninfo を追記する。
  4. プライマリサーバーを自動的に再起動してレプリケーション通信を開始させる。
正解と解説を表示

正解:C

解説:-R--write-recovery-conf)オプションは、スタンバイとして起動するためのトリガーファイルである standby.signal を生成し、postgresql.auto.conf にプライマリへの接続設定(primary_conninfo)を書き込みます(Cが正解)。recovery.conf は PostgreSQL 12 で廃止されたためBは誤りです。

【問2】非同期Streaming Replication環境において、プライマリ側のレプリケーション稼働状況や遅延量を確認するために参照する最も代表的なビューはどれですか。

  1. pg_stat_database
  2. pg_stat_replication
  3. pg_stat_wal_receiver
  4. pg_stat_activity
正解と解説を表示

正解:B

解説:プライマリサーバー側で接続中のスタンバイの状態(statesync_statesent_lsnreplay_lsn 等)を確認するには pg_stat_replication ビューを参照します(Bが正解)。pg_stat_wal_receiver はスタンバイ側で受信状況を確認するためのビューです。

【問3】スタンバイサーバーに対してクライアントから INSERT クエリを発行した場合の挙動として、正しいものはどれですか。

  1. スタンバイサーバー上で更新がコミットされ、プライマリ側へ逆向きにデータが転送される。
  2. 更新クエリは自動的にプライマリサーバーへプロキシ転送されて実行される。
  3. 「リードオンリーのトランザクションでは INSERT を実行できません」というエラーが発生し、処理が拒絶される。
  4. クエリは成功するが、プライマリが再起動されるまでデータはディスクに書き込まれない。
正解と解説を表示

正解:C

解説:Streaming Replicationのスタンバイは読み取り専用(リードレプリカ)として保護されており、直接のデータ更新クエリはエンジンによって即座に拒絶されます(Cが正解)。逆向きの転送や自動プロキシ機能は標準では備わっていません。

8. まとめとシリーズ完結

  • プロセスの連携:プライマリの walsender、スタンバイの walreceiverstartup プロセスの協調により、リアルタイムなWAL同期が成立する。
  • -R と -S による確実な複製:pg_basebackup -R -c fast -S standby_slot_01 により、現行仕様(standby.signalprimary_conninfoprimary_slotname)を自動生成し、待機時間なく安全にクラスタを複製できる。
  • 監視の要:pg_stat_replication および pg_replication_slots を用いて state: streaming と遅延バイト数(replication_lag_bytes)を定常監視する。
  • リードレプリカ保護:スタンバイ側は読み取り専用として安全に保護され、参照負荷分散の役割を果たす。
  • フェイルオーバーの基本:プライマリ障害時はスタンバイ側で SELECT pg_promote(); を発行することで、新プライマリへの昇格が可能。

本記事をもって、「現場で迷わない PostgreSQL環境構築・運用シリーズ」全10回(第00回〜第09回)は完結となります。第01回のアーキテクチャ基礎から始まり、インストール選定、設定チューニング、アクセス制御、権限設計、物理・論理バックアップ、VACUUM内部機構、マイナーおよびメジャーバージョンアップ、そしてストリーミングレプリケーションによるクラスタ冗長化まで、本番運用に必要な実務知識を網羅しました。

PostgreSQL 18 構築・運用ガイド

Kindle書籍版(第7章)のご案内:Patroni + etcd による自動フェイルオーバーHAクラスタ構築

本ブログ記事で解説したストリーミングレプリケーションは冗長化の基礎ですが、手動昇格(pg_promote)運用には「障害検知の遅延」「深夜帯の手動切り替え負担」「誤操作によるスプリットブレイン(両系書き込み)の危険性」という実務上の大きな壁が存在します。商業出版予定のKindle書籍版『現場で困らない PostgreSQL 本番運用大全 —— 4ノード実機で作る高可用性・チューニング・障害復旧』第7章では、ブログ版の内容をさらに発展させ、以下のエンタープライズ高可用性アーキテクチャを完全収録しています。

  • Patroni + etcd による自動フェイルオーバー基盤:分散合意アルゴリズム(Raft)を用いてリーダーノードを厳密に選出し、プライマリ障害時にダウンタイム数十秒で完全無人フェイルオーバーを実行するクラスタ設計。
  • スプリットブレイン(脳分離症候群)の根絶:ネットワーク分断時にも両系書き込みを物理的に防止するウォッチドッグ(Watchdog)およびフェンシング機構。
  • PgBouncer + VIP(仮想IP)によるシームレスな接続切り替え:アプリケーション側の再起動や設定変更を一切伴わず、昇格した新プライマリへクライアント接続を即座にリルートする接続プーリング連携。
  • pg_rewind による旧プライマリの自動再同期:フェイルオーバー後に復旧した旧プライマリを、自動的にタイムライン分岐地点まで巻き戻してスタンバイとしてクラスタへ自己修復・再参加させる運用実務。

24時間365日の安定稼働を実現する真の高可用性環境を構築したいインフラエンジニアの方は、書籍版の内容もあわせてご活用ください。

前の記事