PostgreSQLにおける高可用性のための同期レプリケーション設定
PostgreSQLを高可用性 (HA) 構成にすることは、あらゆるミッションクリティカルなアプリケーションにとって不可欠です。非同期レプリケーションはパフォーマンス上の利点を提供しますが、プライマリサーバーが変更をスタンバイに完全に送信する前に障害が発生した場合、本質的にデータ損失のリスクを伴います。同期ストリーミングレプリケーションは、トランザクションがプライマリと少なくとも1つの指定されたスタンバイサーバーの両方のWAL (Write-Ahead Log) にデータが正常に書き込まれた後で初めてコミットされたと見なされることを保証することで、この問題を解決します。これにより、RPO (Recovery Point Objective) がゼロであることが保証されます。
この包括的なガイドでは、堅牢でデータ損失ゼロのPostgreSQLレプリケーションセットアップを確立するために必要な、不可欠な設定手順を順を追って説明します。この高可用性アーキテクチャを支える、wal_levelやsynchronous_commitなどの重要なパラメータに焦点を当てます。
前提条件
開始する前に、PostgreSQLの同じメジャーバージョンを実行している2つのPostgreSQLサーバー (プライマリとスタンバイ) がセットアップされていることを確認してください。両方のサーバーはネットワーク接続が可能な状態である必要があります。このガイドでは、以下を前提とします。
- プライマリホスト名/IP:
pg_primary - スタンバイホスト名/IP:
pg_standby - レプリケーションユーザー:
repl_user(適切な権限で作成済み) - データベース名:
mydb
ステップ1: プライマリサーバーの設定
プライマリサーバーでは、ストリーミングレプリケーションを有効にし、同期コミットに必要なWrite-Ahead Log (WAL) を管理するために、特定の設定が必要です。
A. プライマリのpostgresql.confの調整
プライマリサーバーのpostgresql.confファイルを編集します。以下のパラメータは、ストリーミングレプリケーションに必須です。
# --- レプリケーションに必須 ---
listen_addresses = '*' # スタンバイからの接続を許可します
wal_level = replica # 'replica'以上である必要があります (例: 'logical')
max_wal_senders = 10 # スタンバイからの最大同時接続数
max_replication_slots = 10 # 永続的なレプリケーションストリームに必要なスロット
# --- 同期コミットに不可欠 ---
synchronous_standby_names = '1 (standby_app_name)' # 必須スタンバイを指定します
# --- オプションですが推奨 ---
wal_log_hints = on # WALボリュームは増加しますが、より安全なレプリケーションのために推奨されます
shared_preload_libraries = 'pg_stat_statements' # 監視を使用している場合
主要パラメータの説明:
wal_level = replica: これにより、スタンバイサーバーがデータベースの状態を再構築できるように、十分な情報がWALに書き込まれることが保証されます。同期コミットの場合、このレベルは最小要件です。synchronous_standby_names: これは、どのスタンバイが書き込みを承認しなければならないかを定義するためのコア設定です。(N (standby_name))構文を使用して定義します。N=1の場合、トランザクションがコミットされる前に、少なくとも1つのスタンバイが書き込みを確定する必要があります。
B. ホストベース認証 (pg_hba.conf) の設定
プライマリサーバーは、スタンバイサーバーからのレプリケーションユーザーがレプリケーション目的で接続することを許可する必要があります。
プライマリのpg_hba.confにエントリを追加します。
# TYPE DATABASE USER ADDRESS METHOD
host replication repl_user pg_standby/32 scram-sha-256
pg_standby/32をスタンバイサーバーの実際のIPアドレスまたはサブネットに置き換えてください。
C. レプリケーションスロットとユーザーの作成
プライマリサーバーのPostgreSQLに接続して、必要なユーザーとレプリケーションスロットを作成します。
1. レプリケーションユーザーの作成:
CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'a_strong_password';
2. レプリケーションスロットの作成:
このスロットは、スタンバイが受信を確認するまでWALセグメントが保持されることを保証し、スタンバイが一時的に切断された場合のデータ損失を防ぎます。
SELECT pg_create_physical_replication_slot('standby_app_name');
- 命名に関する注意: ここで提供される名前 (
standby_app_name) は、プライマリのsynchronous_standby_namesで指定された名前と一致する必要があります。
D. プライマリの再起動
プライマリサーバーのPostgreSQLサービスを再起動して、すべての設定変更を適用します。
sudo systemctl restart postgresql
ステップ2: スタンバイサーバーの設定
スタンバイサーバーは、リカバリ設定を使用してプライマリからWALレコードをストリーミングするように設定されます。
A. ベースバックアップ
ストリーミングを開始する前に、スタンバイはプライマリのデータディレクトリの完全なコピーを必要とします。最初にスタンバイでPostgreSQLを停止します。
sudo systemctl stop postgresql
pg_basebackupを使用してベースバックアップを取得します。必要に応じてパスと接続詳細を置き換えてください。
# pg_basebackupユーティリティを使用した例
pg_basebackup -h pg_primary -D /var/lib/postgresql/15/main/ -U repl_user -P -Xs -R -W
-D: スタンバイ上のターゲットデータディレクトリ。-U: レプリケーションユーザー。-P: 進行状況を表示します。-Xs: ベースバックアップ中に必要なWALファイルを含めます。-R:standby.signalファイルを自動的に作成し、postgresql.auto.conf(またはリカバリ設定) に必要な接続設定を生成します。
B. スタンバイのpostgresql.confの設定
スタンバイでは、postgresql.confがレプリカとして機能することを許可していることを確認してください。ここでの主要な設定は、プライマリで使用されるスロット名と一致するアプリケーション名を指定することです。
# --- スタンバイに必須 ---
primary_conninfo = 'host=pg_primary port=5432 user=repl_user password=a_strong_password application_name=standby_app_name'
hot_standby = on # リカバリ/スタンバイモード中に読み取りクエリを許可します
C. スタンバイの起動
スタンバイサーバーのPostgreSQLサービスを開始します。
sudo systemctl start postgresql
ステップ3: 同期コミットの検証とテスト
両方のサーバーが稼働したら、接続を確認し、同期動作をテストします。
A. レプリケーションステータスの確認
プライマリデータベースに接続し、pg_stat_replicationビューを確認します。
SELECT client_addr, application_name, state, sync_state FROM pg_stat_replication;
sync_stateがsyncであるstandby_app_nameのエントリが表示されるはずです。
B. 同期コミットのテスト
PostgreSQLがどの程度厳密に待機するかを決定するグローバルパラメータはsynchronous_commitです。RPO=0の場合、同期を強制する値を使用する必要があります。
1. グローバルな動作の設定
ステップ1で示したようにプライマリでsynchronous_standby_namesを設定した場合、synchronous_commitがon (デフォルト) またはremote_writeに設定されていると、デフォルトの動作で必要なスタンバイの待機が強制されます。
最も強力な保証を得るには、postgresql.confでremote_writeまたはremote_apply (スタンバイがデータをディスクにフラッシュするだけでなく、受信したことを確認する必要がある場合) を明示的に設定します。
# プライマリのpostgresql.conf内
synchronous_commit = remote_write
警告:
synchronous_commit = remote_writeまたはonを設定すると、非同期モード (offまたはlocal) と比較してトランザクションのレイテンシが大幅に増加します。このレイテンシは、プライマリと同期スタンバイ間のネットワーク速度に直接相関します。
2. トランザクション内でのテスト
グローバルな設定変更を必要とせずに、トランザクション的にテストするには、セッションごとまたはトランザクションごとに設定できます。
-- プライマリに接続
BEGIN;
SET LOCAL synchronous_commit = remote_write;
INSERT INTO sales (item, amount) VALUES ('Widget A', 100);
-- このINSERTは、「standby_app_name」が受信を確認するまでブロックされます。
COMMIT;
-- COMMITは、スタンバイがWALの書き込みを承認した後にのみ成功します。
トランザクション中に同期スタンバイへの接続が失われた場合、プライマリは無期限に待機するか (スタンバイがクリーンに切断された場合)、synchronous_commit_fallback_on_error設定に基づいてフォールバックします (デフォルトはonであり、プライマリが同期ステータスを確認できない場合、トランザクションが失敗するかハングする可能性があります)。
同期HAのベストプラクティス
- 専用のスタンバイを使用する: 物理的に近い (低レイテンシ) スタンバイのみを同期レプリケーションリストに割り当ててください。高レイテンシは書き込みパフォーマンスに深刻な影響を与えます。
- レプリケーション遅延を監視する: 同期モードであっても、スタンバイの遅延を監視してください。技術的には「同期」だがWALの処理に時間がかかりすぎる遅いスタンバイは、ユーザーエクスペリエンスに影響を与える可能性があります。
- 接続フォールバック:
synchronous_commit_fallback_on_error設定を理解してください。offに設定されている場合、コミット中に同期スタンバイとの通信に失敗すると、プライマリ上のトランザクションが失敗し、潜在的なデータ発散を防ぎますが、即座に可用性に影響を与えます。 - 複数のスタンバイを使用する: 同期セットアップ内で最大の冗長性を得るには、
synchronous_standby_names = '2 (standby1, standby2)'を設定して、2つの異なるスタンバイからのコミットを要求します。