本記事では、PostgreSQL 9.0 より導入された「ストリーミングレプリケーション」の概要、構築方法について紹介します。
記事内の各リンクについて、特記のないものはすべてPostgreSQL16.0文書の該当項目へのリンクとなっております。
ストリーミングレプリケーションの概要
ストリーミングレプリケーションとは
ストリーミングレプリケーションとは、PostgreSQL 9.0以降で利用できる本体組み込みのレプリケーション機能です。
本機能はWAL(Write Ahead Logging)をベースに実現しています。
具体的には、プライマリが送信したWALをスタンバイ側が受信し、スタンバイ側でそのWALをリプレイすることによりプライマリと同じ状態を保つ(リカバリし続ける)、という仕組みになっています。
PostgreSQL16.0文書 パートIII.サーバの管理 27.2.5 ストリーミングレプリケーション
プライマリとスタンバイ
プライマリやスタンバイといったサーバに対する呼び方を整理します。
ストリーミングレプリケーションを構成するサーバは、以下のふたつの役割に分けられます。
プライマリ(Primary) | クライアントから直接データ更新を受けつけるサーバ |
スタンバイ(Standby) | プライマリ側のデータ変更を追跡するサーバ |
これらの関係は柔軟であり、プライマリから複数のスタンバイを並列で繋ぐ構成や、スタンバイをさらに別のスタンバイに接続する構成などを組み合わせることも可能です。
本記事では前者をマルチスタンバイ構成、後者をカスケード構成と呼ぶことにし、以下の図に表します。
ストリーミングレプリケーション導入の背景
PostgreSQL 9.0でストリーミングレプリケーションが実装されるまで、PostgreSQLでのレプリケーションには外部ツールを用いてきました。
この外部ツールの多くは「プライマリに実行されたものと同様のSQLをスタンバイに転送し実行する」ことでレプリケーションを実現しているものでした。
当時のPostgreSQLコア開発コミュニティでは用途に合わせて様々なレプリケーションツールが開発されていましたが、それらのプロダクトもいつまで使えるかの保証はなく、デファクトスタンダードとなるツールが必要とされる状況にありました。
こうした背景から、ストリーミングレプリケーションはPostgreSQLにおけるレプリケーションのデファクトスタンダードを目指して開発されました。
前述のようにストリーミングレプリケーションはWALベースの仕組みであり、その点においてそれまでのSQLベースの外部ツールとは異なります。
SQLベースでは、複雑なクエリの処理によりレプリケーションの遅延が発生したり、SQL文が意図しない結果を引き起こしデータ整合性のリスクが生じる場合があります。
こうした点において、WALベースのストリーミングレプリケーションでは、SQLベースのレプリケーションと比べ一般的に下記のようなメリットがあります。
データ整合性の確保 | 物理レベルのデータを転送するため、データベースの完全なコピーとなる |
処理の低遅延性 | SQLステートメントを実行しないため、レプリケーションの遅延が少ない場面が多い |
ただし、ストリーミングレプリケーションでは物理レベルでのデータ転送が行われるため、ストレージやネットワーク帯域幅の消費が増加する可能性があります。
プライマリ・スタンバイ間のネットワーク帯域が不足している場合だと、データ転送量によっては遅延が発生することを考慮した方がよいでしょう。
それでも、データの整合性を重視する場面では、ストリーミングレプリケーションは優れた選択肢といえます。
ストリーミングレプリケーションでできること
ストリーミングレプリケーションによって、実現できる用途の代表は以下の通りです。
高可用性(HA) | システム障害に備えた冗長化 |
災害対策(DR) | 遠隔地のスタンバイでデータの安全性確保 |
負荷分散 | 読み取り専用ノードを用いてクエリ負荷を分散 |
注意点として、障害検知機能やそれに伴う自動フェイルオーバー機能、及び負荷分散の自動化機能はPostgreSQL本体は提供していません。
これらの機能を求める場合は、外部のHAソフトウェアやクラスタリングソフトウェアなどと組み合わせるなどの対応が必要となります。
また、プライマリのデータをコピーするという点では、バックアップと役割が似ているようにも見えますが、レプリケーションの場合は誤操作なども含めレプリケーションされてしまうため、一般にバックアップにはなりません。
同期/非同期の違いについて
同期と非同期の違いは、スタンバイでWALがどのような状態になったらクライアントにコミット完了を通知するかです。
以下にそれぞれの特徴を示します。
同期モード | |
---|---|
通知タイミング | スタンバイでWALが正常に書き込みされたことを待ってから、クライアントにコミット完了を通知する |
データの整合性 | プライマリ側でコミット済のトランザクションの内容はプライマリ・スタンバイ両方に書き込まれる |
処理性能 | レプリケーション完了まで処理が続くため、パフォーマンスが悪化する可能性がある |
非同期モード | |
---|---|
通知タイミング | スタンバイで行われるWALに対する処理を待たずに、クライアントにコミット完了を通知する |
データの整合性 | プライマリでのコミットがスタンバイでのコミットを待たないため、プライマリの故障時にデータ損失のリスクが高まる |
処理性能 | 高速で効率的な動作が可能で、遅延が少ないため同期に比べてパフォーマンスの向上が見込まれる |
ここでは詳細は割愛しますが、同期/非同期はプライマリのsynchronous_commitパラメータなどで設定します(デフォルトは非同期設定)。
ロジカルレプリケーションとストリーミングレプリケーションの違いについて
ストリーミングレプリケーションの特徴は、データベースクラスタ全体が物理レベルでレプリケーションされることで、プライマリとスタンバイのデータを同じ状態に保てることです。
あくまでデータベースクラスタ全体でのレプリケーションであるため、特定のテーブルや列のみを対象として限定することはできません。
また、構造が異なる環境どうしではレプリケーションできず、異なるメジャーバージョン間やOS間では利用できません。
対してロジカルレプリケーションでは、データベースやテーブル単位でWALの変更情報を論理的なレベルに変換(デコード)して転送します。
こちらは論理的な変更を行うため、異なるメジャーバージョン間やOS間でもレプリケーション可能です。
また、特定のテーブルや列のみを対象にしたレプリケーションも可能となっております。
これらの特徴を踏まえ、用途によってどちらのレプリケーションを利用するかを選ぶことで、より効率的なデータベースの運用ができるでしょう。
ストリーミングレプリケーション | 高可用性構成や参照負荷の分散に適している |
ロジカルレプリケーション | データ移行や特定データの統合・集約に適している |
ロジカルレプリケーションの詳細については、以下の記事をご覧ください。
SRA OSS Tech Blog: ロジカルレプリケーションの紹介(1)
ストリーミングレプリケーションの構築
実際にストリーミングレプリケーションが動作する環境を構築していきます。
ここではプライマリに対してスタンバイは一台、非同期モードでストリーミングレプリケーションを構成します。
今回ご紹介する検証環境は以下の通りです。
ホストOS | Rocky Linux 9.5 |
DBMS | PostgreSQL 16.8(PGDGのリポジトリRPMからインストール) スーパーユーザー(管理ユーザ)は「postgres」 |
※PGDG:PostgreSQL Global Development Group
①プライマリの準備
ストリーミングレプリケーション構築の準備として、プライマリ側の設定を行います。
まずはプライマリのデータベースクラスタを作成します。
以降の説明では、/usr/pgsql-16/bin/ に PATH を通してある前提で話を進めます。
$ initdb $ ls /var/lib/pgsql/16/data
・postgresql.confの設定(プライマリ)
下記のパラメータはどちらもデフォルト値となっています。
$ cat /var/lib/pgsql/16/data/postgresql.conf #wal_level = replica #max_wal_senders = 10
ここではwal_levelにreplicaを指定していますが、logicalを指定しても動作には問題ありません。
replicaに設定することで、WALアーカイビングおよびレプリケーションをサポートするために十分なデータを書き出します。
max_wal_sendersはWALを送信するプロセスの最大数を設定します。
スタンバイを複数台接続する場合は、適宜この値も調整するようにしてください。
・pg_hba.confの設定
後述するpg_basebackupの実行クライアントやスタンバイは、プライマリに対してレプリケーション接続を行います。
下記の通り、クライアント認証はデフォルトで仮想的なデータベース名replicationを指定し設定されています。
$ cat /var/lib/pgsql/16/data/pg_hba.conf # Allow replication connections from localhost, by a user # with the replication privilege. local replication all trust host replication all 127.0.0.1/32 trust host replication all ::1/128 trust
ここでは検証のために認証をtrustに設定していますが、レプリケーション接続ができるとデータの変更内容を受信できるため、現実的ではありません。
実際にはSCRAM-SHA-256を指定するなど、セキュリティを考慮した設定にしてください。
②スタンバイの準備
プライマリのデータベースクラスタをコピーし、スタンバイとして接続できるようにします。
・プライマリを起動する
$ pg_ctl start -D /var/lib/pgsql/16/data
・プライマリからベースバックアップを取得する
$ pg_basebackup -p 5432 -D /var/lib/pgsql/16/data2 -R -P 24549/24529 kB (100%), 1/1 tablespace
-Rオプションを指定すると、レプリケーションに必要な最低限の設定がpostgresql.auto.confに追加され、standby.signalが生成されます。
・postgresql.confの設定(スタンバイ)
$ vi /var/lib/pgsql/16/data2/postgresql.conf #hot_standby = on port = 5433
hot_standbyはリカバリ中のスタンバイが接続を受けつけ、参照ができるかを指定するパラメータです。(デフォルトでon)
portは通常変更する必要はありません。
ここでは同じホスト上でスタンバイを起動するため、プライマリのportと重複しないように変更しています。
・postgresql.auto.confの確認
編集は行わず、関連箇所のみ確認します。
前述のようにpg_basebackupで-Rオプションを指定して実行すると、接続先のプライマリの情報をもとにスタンバイの設定が自動的に追加されます。
$ cat /var/lib/pgsql/16/data2/postgresql.auto.conf primary_conninfo = 'user=postgres passfile=' '/var/lib/pgsql/.pgpass' '(以下略)'
primary_conninfoは接続先のプライマリの接続文字列を指定するパラメータです。
・standby.signalの確認
standby.signalはスタンバイとして起動するかを指定する空のファイルです。
データベースクラスタ内にこのファイルが存在すると、サーバはスタンバイモードで起動されます。
ここではpostgresql.auto.confと同様に、pg_basebackupで-Rオプションを指定して実行したことで、自動的に生成されています。
$ ls /var/lib/pgsql/16/data2/standby.signal
③レプリケーションの動作確認
これでストリーミングレプリケーションの準備は整いました。
ここからは以下の検証を行い、レプリケーションの動作を確認します。
- プライマリに挿入したデータが、スタンバイにコピーされているか
- スタンバイでデータの挿入や削除が行えないようになっているか
それでは検証を実施していきましょう。
まずはスタンバイを起動します。
$ pg_ctl start -D /var/lib/pgsql/16/data2
次にプライマリに検証用のデータベース及びテーブルを作成し、データを挿入します。
$ createdb -p 5432 repdb $ psql -p 5432 repdb =# CREATE TABLE t (c int); =# INSERT INTO t VALUES (1); =# SELECT * FROM t; c --- 1 (1 row)
スタンバイに上記のデータが同期されているか、参照で確認します。
$ psql -p 5433 repdb =# SELECT * FROM t; c --- 1 (1 row)
プライマリで挿入したデータがスタンバイでも参照できました。
次にスタンバイのデータに挿入を行えるかを検証してみます。
=# INSERT INTO t VALUES (2); ERROR: cannot execute INSERT in a read-only transaction
このようにスタンバイに挿入を試みてもエラーになります。
ストリーミングレプリケーションの基本的な動作を確認することができました。
また、レプリケーションの確認として、pg_stat_replicationビューにエントリが存在するも確かめてみましょう。
$ psql -p 5432 =# SELECT * FROM pg_stat_replication; -[ RECORD 1 ]----+------------------------------ ~省略~ state | streaming sync_state | async
上記の state が streaming であることから、レプリケーションが正常に動作していると判断できます。
また、sync_state が async(非同期)であることも確認できます。
スタンバイ昇格と復旧手順
「プライマリが何らかの不具合で停止してしまった」
そんな時のために、ここではスタンバイをプライマリに昇格する手順をご紹介します。
①スタンバイの昇格手順
スタンバイをプライマリに昇格させる方法は複数あります。
- コマンドラインでpg_ctl promote用いる方法
- SQLでpg_promote関数を実行する方法
- promote_trigger_fileを用いる方法(PostgreSQL 15以前)
ここでは代表例として、コマンドラインでpg_ctl promoteを実行する方法を紹介します。
$ pg_ctl promote -D /var/lib/pgsql/16/data2 waiting for server to promote.... done server promoted
これでスタンバイが新プライマリに昇格しました。
新プライマリが更新を受けつけることを確認します。
$ psql -p 5433 repdb =# INSERT INTO t VALUES (3); =# SELECT * FROM t; c --- 1 3 (2 rows)
新プライマリでデータの更新ができました。
一応データベースクラスタ内のstandby.signalの有無も確認してみましょう。
$ ls /var/lib/pgsql/16/data2/standby.signal
スタンバイではなくなったことで、当該ファイルは除去されています。
なお、promote後は旧プライマリとのレプリケーションはなくなり、改めて新プライマリのシングル構成となっています。
②旧プライマリを新スタンバイにする手順と注意点
新スタンバイが昇格後、旧プライマリ側で一切WAL書き込みが発生していない状況なら、旧プライマリを新スタンバイとして組み込み可能です。
ですので、旧プライマリと旧スタンバイを単純に入れ替える場合ならば、旧プライマリを停止したうえで旧スタンバイを新プライマリに昇格するのが確実です。
しかし旧プライマリと新プライマリの間にデータのずれがある状況では、単純に旧プライマリを新スタンバイとしてレプリケーションを構築することができません。
その場合、以下の方法でスタンバイを構築する必要があります。
- 再度新プライマリから pg_basebackup で ベースバックアップを取得しなおす
- pg_rewindで旧プライマリを新プライマリと同期させる
・pg_basebackupを用いる場合
一つ目の方法のベースバックアップに関しては、前述の「ストリーミングレプリケーションの構築」で紹介したと同様の手順で実行できます。
基本的にはこちらの方法で愚直にバックアップを取り直したうえで、改めてレプリケーションを構成することが安全で確実といえます。
・pg_rewindを用いる場合
二つ目の方法のpg_rewindを使用するには以下の条件をクリアしている必要があります。
- 事前にwal_log_hintsをonに設定しておく。(デフォルトはoff)
もしくはpg_checksums -eでデータチェックサムを有効にしておく。(-e オプションで有効化) - full_page_writesをonにしておく。(デフォルトでon)
これらの設定は後から設定しても、その直後はpg_rewindを使用できないため、あらかじめ設定しておくことを忘れないようにしてください。
データベースクラスタが大きい場合には、pg_rewindの方がベースバックアップに比べて復旧時間が短縮できます。
これはpg_rewindの方が差分同期で済むためです。
pg_rewindとpg_basebackupのどちらの手段を選択するかは、ストリーミングレプリケーションを構成する際にあらかじめ想定しておくとよいでしょう。
最後に
本記事ではストリーミングレプリケーションの概要と、基本的な構築手順をご紹介いたしました。
次回以降では、
・同期と非同期についての詳細
・コンフリクトについて
・統計情報ビューの確認方法
・フェイルオーバーについて
など運用に関わる情報をお伝えする予定です。