2026年8月4日

2026年8月4日

PostgreSQLのレプリケーション設定方法(ストリーミングレプリケーションの構築・確認・切り替え)

PostgreSQL のレプリケーションは、プライマリ(書き込みを受けるサーバー)で WAL(更新の記録)を送れるように設定し、スタンバイ(複製先)を pg_basebackup で作って接続する、という手順で構築できます。この記事では、最もよく使われる物理的なストリーミングレプリケーションについて、プライマリとスタンバイの設定、動作の確認方法、レプリケーションスロットの注意点、同期・非同期の選び方、障害時の切り替えまでを説明します。テーブル単位で複製する論理レプリケーションとの違いにも触れます。

WordPress のコアは MySQL・MariaDB を対象にしており、PostgreSQL は公式にはサポートされていません。WordPress のデータベースを冗長化したい場合は MySQL のレプリケーションを使います。この記事は、WordPress と並べて動かす別のアプリケーションなどで PostgreSQL を使う場面を想定しています。

構成と前提

ここでは次の構成を例にします。IP アドレス、バージョン番号、ディレクトリは環境に合わせて読み替えてください。

  • プライマリ:192.0.2.10
  • スタンバイ:192.0.2.20
  • 両方とも同じメジャーバージョンの PostgreSQL(例として 17)を Ubuntu にパッケージでインストール済み

物理レプリケーションでは、データベースクラスタ全体をそのまま複製するため、プライマリとスタンバイのメジャーバージョンと CPU アーキテクチャをそろえる必要があります。作業の前に、プライマリのバックアップ(pg_dump や pg_basebackup による取得)を済ませ、可能ならステージング環境で一度手順を通してから本番に適用してください。

プライマリの設定

レプリケーション用のロールを作る

レプリケーション専用のロールを作ります。パスワードは SQL 文に直接書くとサーバーログやコマンド履歴に残ることがあるため、psql の \password で対話的に設定します。

CREATE ROLE replicator WITH REPLICATION LOGIN;
\password replicator

postgresql.conf

Ubuntu のパッケージでは /etc/postgresql/17/main/postgresql.conf が設定ファイルです。

listen_addresses = 'localhost,192.0.2.10'
wal_level = replica            # PostgreSQL 10 以降は既定値
max_wal_senders = 10           # 同時に接続できるスタンバイ・バックアップの数
max_replication_slots = 10
max_slot_wal_keep_size = 10GB  # スロットが保持する WAL の上限(PostgreSQL 13 以降)

wal_level、max_wal_senders、max_replication_slots は PostgreSQL 10 以降、既定値のままでも物理レプリケーションに足りる値になっています。変更が必要なのは主に listen_addresses です。max_slot_wal_keep_size の意味は後の「レプリケーションスロットの注意点」で説明します。

pg_hba.conf

スタンバイからのレプリケーション接続を許可します。database の欄に書く replication は、レプリケーション接続を表す特別なキーワードです。

# TYPE     DATABASE     USER        ADDRESS          METHOD
hostssl    replication  replicator  192.0.2.20/32    scram-sha-256

許可する接続元はスタンバイの IP アドレスだけに絞ります。hostssl は SSL 接続だけを受け付ける指定で、サーバー側で SSL が有効になっている必要があります。SSL を設定していない検証環境では host にしますが、インターネットを経由する経路では暗号化してください。あわせて、ファイアウォールで 5432 番ポートをスタンバイからだけ開けます。

設定を変えたら再起動し、ログにエラーが出ていないかを確認します。

sudo systemctl restart postgresql
sudo tail -n 50 /var/log/postgresql/postgresql-17-main.log

スタンバイの作成

スタンバイ側では、PostgreSQL を停止し、既存のデータディレクトリを別名に退避してから、プライマリの内容を pg_basebackup で複製します。スタンバイ側で実行していること、パスが正しいことをよく確認してから実行してください。退避したディレクトリは、スタンバイが正常に動くのを確かめてから削除します。

sudo systemctl stop postgresql
sudo -u postgres mv /var/lib/postgresql/17/main /var/lib/postgresql/17/main.old

sudo -u postgres pg_basebackup \
  -h 192.0.2.10 -U replicator \
  -D /var/lib/postgresql/17/main \
  -X stream -R -C -S standby1 -P

各オプションの意味は次のとおりです。

  • -X stream:複製中に発生した WAL も並行して受け取る
  • -R:スタンバイとして起動するための standby.signal ファイルと、接続情報(primary_conninfo)を書いた postgresql.auto.conf を自動作成する(PostgreSQL 12 以降の形式)
  • -C -S standby1:プライマリに standby1 という名前のレプリケーションスロットを作り、それを使う
  • -P:進捗を表示する

パスワードの入力を求められたら、プライマリで設定したものを入力します。自動化する場合は、postgres ユーザーのホームに .pgpass を置き、権限を 600 にします。完了したら sudo systemctl start postgresql でスタンバイを起動します。

動作を確認する

プライマリで、スタンバイが接続しているかを確認します。

SELECT client_addr, application_name, state, sync_state,
       sent_lsn, replay_lsn, replay_lag
FROM pg_stat_replication;

state が streaming であれば、WAL が送られています。replay_lag はスタンバイでの反映の遅れの目安です。

スタンバイでは次を確認します。

SELECT pg_is_in_recovery();                      -- true ならスタンバイとして動作中
SELECT status, sender_host FROM pg_stat_wal_receiver;
SELECT now() - pg_last_xact_replay_timestamp() AS replay_delay;

最後に、プライマリでテスト用のテーブルを作って行を入れ、スタンバイで読めることを確認します。スタンバイは読み取り専用なので、書き込もうとするとエラーになります。これは正しい動作です。

レプリケーションスロットの注意点

レプリケーションスロットは、スタンバイがまだ受け取っていない WAL をプライマリに残しておく仕組みです。スタンバイが一時的に止まっても、復帰後に続きから追いつけます。

その反面、スタンバイが長時間止まったままだと、プライマリに WAL がたまり続け、ディスクがいっぱいになってプライマリまで止まる危険があります。PostgreSQL 13 以降なら、先ほどの max_slot_wal_keep_size で保持する量に上限を設けられます。上限を超えるとそのスロットは使えなくなり、スタンバイは作り直しになりますが、プライマリは守られます。

スロットの状態は定期的に監視します。使わなくなったスタンバイのスロットは削除してください。

SELECT slot_name, active, wal_status,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots;

-- 不要になったスロットの削除
SELECT pg_drop_replication_slot('standby1');

同期レプリケーションにするか

既定は非同期レプリケーションです。プライマリはスタンバイへの反映を待たずにコミットを完了するため速い一方、プライマリが故障した瞬間の直前の更新が、スタンバイに届いていない可能性があります。

同期レプリケーションにすると、指定したスタンバイが WAL を受け取るまでコミットが完了しません。

# プライマリの postgresql.conf
synchronous_standby_names = 'FIRST 1 (standby1)'

ここでの名前は、スタンバイの primary_conninfo に書いた application_name と一致させます(pg_basebackup -R で作った接続情報には自動で入らないことがあるため、確認して追記します)。注意点は、同期先のスタンバイが止まると、プライマリの書き込みが待たされたまま進まなくなることです。同期にするなら、スタンバイを2台以上用意するなどの対策を合わせて検討してください。

プライマリ障害時の切り替え

プライマリが復旧できないときは、スタンバイを昇格させて新しいプライマリにします。

-- スタンバイで実行(PostgreSQL 12 以降)
SELECT pg_promote();

昇格後は、アプリケーションの接続先を新しいプライマリに変えます。古いプライマリをそのまま起動すると、2台が両方とも書き込みを受け付ける状態になり、データが食い違います。古いプライマリは停止したままにし、スタンバイとして戻すときは pg_rewind か pg_basebackup で作り直してください。

切り替えを自動化したい場合は、Patroni などの高可用性ツールや、クラウドのマネージドサービスの機能を使う方法があります。自動化の仕組みは誤った切り替えを防ぐ設計が難しいため、各ツールの公式ドキュメントに沿って構築し、切り替えの訓練をしておくことが大切です。

論理レプリケーションとの違い

論理レプリケーション(PostgreSQL 10 以降)は、CREATE PUBLICATION と CREATE SUBSCRIPTION でテーブル単位に変更を送る仕組みです。メジャーバージョンが異なるサーバー間でも使えるため、バージョンアップの移行や、一部のテーブルだけを別のサーバーに渡す用途に向いています。ただし、プライマリ側で wal_level = logical が必要なほか、テーブル定義の変更(DDL)は複製されないなど制約があります。シーケンスの値の扱いなど、細かな対応範囲はバージョンによって変わっているため(2026年10月現在)、使うバージョンのドキュメントで確認してください。

レプリケーションはバックアップの代わりにならない

レプリケーションは、誤って実行した DELETE や DROP TABLE もそのままスタンバイに反映します。サーバー障害への備えにはなりますが、操作ミスやデータ破損からの復旧には使えません。pg_dump による論理バックアップや、ベースバックアップと WAL アーカイブによる時点復旧の仕組みを、別に用意してください。

まとめ

  • 物理ストリーミングレプリケーションは、プライマリで接続を許可し、スタンバイを pg_basebackup -R で作れば動く
  • レプリケーション用のロールは専用にし、接続元の IP を絞り、経路は SSL で暗号化する
  • pg_stat_replication と pg_is_in_recovery() で状態と遅れを確認する
  • スロットは WAL をため込むので、max_slot_wal_keep_size の設定と監視を忘れない
  • 同期レプリケーションはデータを守れるが、スタンバイ停止で書き込みが止まる点に注意
  • 切り替え後の古いプライマリは起動させない。レプリケーションとは別にバックアップを取る

お気軽にご相談ください

お見積りへ お問い合わせへ
▲