Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

PostgreSQL9.0でStreaming Replication + Hot Standbyを試してみた

2010/09/20に正式リリースされたPostgreSQL9.0ですが、大幅な機能強化がなされてますね。

個人的に大きなポイントだと思っている項目がいくつかありますが、その中でも

の組み合わせは実運用でもかなり使いたいと思っています。
ストリーミングレプリケーションは、WAL(アーカイブログ)の転送タイミングがより細かくなったことで、レプリケーションしているDBのスタンバイ側の更新遅延が少なくなるというもの。ホットスタンバイは、同じくスタンバイ側で参照系のクエリを受け付けられるようになるというものです。以前試したウォームスタンバイの場合、スタンバイ中はDB接続できませんでしたから、レプリケーションしつつ参照用DBとして活用できるようになりそうです。
で、ググッてみればすでにいろいろなエントリがあるんですが、"ストリーミングレプリケーション+ホットスタンバイ"という構成を実際に試してみました。

なお、間違いがあれば指摘してください;-)

環境

  • CentOS5.5 on VMware Player
  • RPMPostgreSQL 9.0(PGDG)
  • 以下の2つの構成で手順を確認
    • 同一ホストの別ポートでマスタ/スタンバイを構築
    • 2つのホストにマスタ/スタンバイを構築

大まか流れ

構築手順はウォームスタンバイの構築手順とほぼ同じで、postgresql.conf、pg_hba.conf、recovery.confの設定が若干変わるだけです。
以下、構築手順。

PostgreSQL 9.0のインストール

http://yum.pgrpms.org/howtoyum.phpを参考にPGDGリポジトリ用のyum設定を追加し、インストール。

$ wget http://yum.pgrpms.org/reporpms/9.0/pgdg-centos-9.0-2.noarch.rpm
$ sudo rpm -ivh pgdg-centos-9.0-2.noarch.rpm
$ sudo yum install -y postgresql90 postgresql90-server
$ 

コマンド類は/usr/pgsql-9.0/binにインストールされるので、環境変数PATHを設定しない限り、フルパスでコマンドを指定する必要があります。

マスター側DBの構築

SUPERUSER権限を持ったユーザー"repuser"、データベース名"hotstandby"(オーナーはrepuser)で作成。SUPERUSER権限はストリーミングレプリケーション利用時に必要になります。
postgresql.confは、以下の内容を追記。

  • ホットスタンバイ用のWALを出力する(wal_level = hot_standby)
  • アーカイブモードを有効にする(archive_mode = on)
  • ホットスタンバイDBからの最大接続数を10にする(max_wal_senders = 10)
  • マスタ側が保存しておく最小のログファイル数(wal_keep_segments = 100)

その他詳細はPostgreSQL: Documentation: 9.0: Write Ahead Logを参照のこと。

$ BIN_PATH=/usr/pgsql-9.0/bin/
$ PGBASE=/var/lib/pgsql/9.0
$ \rm -rf $PGBASE/data
$ $BIN_PATH/initdb -D $PGBASE/data
$ sudo /sbin/service postgresql-9.0 start
$ createuser -DRs repuser
$ createdb -O repuser hotstandby
$ CONF_FILE=$PGBASE/data/postgresql.conf
$ echo "listen_addresses = '*'" >> $CONF_FILE
$ echo "wal_level = hot_standby" >> $CONF_FILE
$ echo "archive_mode = on" >> $CONF_FILE
$ echo "archive_command = 'cp -i %p /var/lib/pgsql/9.0/backups/%f </dev/null'" >> $CONF_FILE
$ echo "max_wal_senders = 10" >> $CONF_FILE
$ echo "wal_keep_segments = 100" >> $CONF_FILE
$ 

次に、ストリーミングレプリケーション用の認証設定です。ここでは、database名として"replication"を指定する必要があります。

$ CONF_FILE=$PGBASE/data/pg_hba.conf
$ echo "host    replication     repuser         127.0.0.1/32            trust" >> $CONF_FILE
$ 

設定が終わったので再起動します。

$ sudo /sbin/service postgresql-9.0 restart
$ 

スタンバイ側DBの構築

スタンバイ側DBの構築手順は、ウォームスタンバイの場合と同様、マスターDBのバックアップから初期DBを構築し、スタンバイ用の設定をおこなう、というものです。
まずは、同一ホスト・別ポートにスタンバイDBを構築してみます。
先ほど作成したマスターDBのバックアップを取得し、初期DBを構築します。最後のperlコマンド群で、マスターDB用に設定した項目をコメントアウトしています。

$ BIN_PATH=/usr/pgsql-9.0/bin/
$ PGBASE=/var/lib/pgsql/9.0
$ PORT=5433
$ $BIN_PATH/psql -c "select pg_switch_xlog();"
$ $BIN_PATH/psql -c "select pg_start_backup('for replication')" hotstandby
$ cd $PGBASE/data
$ 
$ tar zcf $PGBASE/backups/data.tgz *
$ $BIN_PATH/psql -c "select pg_stop_backup()" hotstandby
$ cd $PGBASE/
$ mkdir data_$PORT
$ chmod 700 data_$PORT
$ tar zxf backups/data.tgz -C data_$PORT/
$ rm -r data_$PORT/pg_xlog
$ mkdir -p data_$PORT/pg_xlog/archive_status/
$ rm data_$PORT/postmaster.pid
$ 
$ perl -i -p -s -e "s/^archive_/#archive_/g" data_$PORT/postgresql.conf
$ perl -i -p -s -e "s/^wal_level/#wal_level/g" data_$PORT/postgresql.conf
$ perl -i -p -s -e "s/^max_wal_senders/#max_wal_senders/g" data_$PORT/postgresql.conf
$ perl -i -p -s -e "s/^wal_keep_segments/#wal_keep_segments/g" data_$PORT/postgresql.conf
$ 

次にスタンバイ用の設定。ホットスタンバイを有効にする設定を追加。

$ echo "hot_standby = on" >> data_$PORT/postgresql.conf
$ 

もう一つ。WALからリカバリするための設定を記述したrecovery.confを作成します。primary_conninfoには、マスターDBのホスト、ポート番号、レプリケーション用のユーザー名を指定します。また、trigger_fileには、スタンバイからマスターに昇格させる際の"トリガー"となるファイルを指定します。個々で指定したファイルが存在する場合、スタンバイDBはマスターDBに昇格し、

となります。

$ RECOVERY_CONF=$PGBASE/data_$PORT/recovery.conf
$ echo "standby_mode = 'on'" >> $RECOVERY_CONF
$ echo "primary_conninfo = 'host=127.0.0.1 port=5432 user=repuser'" >> $RECOVERY_CONF
$ echo "restore_command = 'cp $PGBASE/backups/%f %p'" >> $RECOVERY_CONF
$ echo "trigger_file = '$PGBASE/trigger_file'" >> $RECOVERY_CONF
$ 

設定が終わったら、スタンバイDBを起動します。

$ sudo echo "PGDATA=/var/lib/pgsql/9.0/data_5433" > /etc/sysconfig/pgsql/postgresql-9.0.5433
$ sudo echo "PGPORT=5433" >> /etc/sysconfig/pgsql/postgresql-9.0.5433
$ sudo /sbin/service postgresql-9.0.5433 start
$ 

起動後、スタンバイ側のログ($PGDATA/pg_log/*)を確認し、

LOG:  ストリーミングレプリケーションがプライマリに無事接続できました

と出力されていればOKです。
もし、

FATAL:  pg_hba.conf にホスト"127.0.0.1"、ユーザ"repuser", SSL無効用のエントリがありません

と出力されている場合、マスター側のpg_hba.confを確認します。
正しく起動されているようなら、psqlコマンドでスタンバイ側に接続できるか確認しておきます。

$ psql -U repuser hotstandby -p 5433
psql (9.0.0)
"help" でヘルプを表示します.

hotstandby=# \q
$ 

動作確認

マスター側で適当にテーブルを作成してデータを登録し、スタンバイ側に正しく反映されているかどうかを見てみます。

$ PGBASE=/var/lib/pgsql/9.0
$ $BIN_PATH/psql -U repuser hotstandby -c "create table test01 (col01 integer not null PRIMARY KEY, col02 text)"
NOTICE:  CREATE TABLE / PRIMARY KEYはテーブル"test01"に暗黙的なインデックス"test01_pkey"を作成します
CREATE TABLE
$ $BIN_PATH/psql -U repuser hotstandby -c "insert into test01 (col01) values (1)"
INSERT 0 1
$ $BIN_PATH/psql -U repuser hotstandby -c "insert into test01 (col01) values (2)"
INSERT 0 1
$ $BIN_PATH/psql -U repuser hotstandby -c "insert into test01 (col01) values (3)"
INSERT 0 1
$ $BIN_PATH/psql -U repuser hotstandby -c "select * from test01"
 col01 | col02
-------+-------
     1 |
     2 |
     3 |
(3)

$ $BIN_PATH/psql -U repuser hotstandby -p 5433 -c "select * from test01"
 col01 | col02
-------+-------
     1 |
     2 |
     3 |
(3)

$ 

スタンバイ側は更新できない

スタンバイ側は読み込み専用なので、直接更新はできません。

$ $BIN_PATH/psql -U repuser hotstandby -p 5433 -c "insert into test01 (col01) values (1)"
ERROR:  リードオンリーのトランザクションでは INSERT を実行できません
$ 

スタンバイが停止している間に更新された場合

スタンバイが停止している間に更新された場合も問題なくリカバリされます(まあ当然ですが。。。)。

$ sudo /sbin/service postgresql-9.0.5433 stop
$ $BIN_PATH/psql -U repuser hotstandby -c "insert into test01 (col01) values (4)"
INSERT 0 1
$ $BIN_PATH/psql -U repuser hotstandby -c "insert into test01 (col01) values (5)"
INSERT 0 1
$ $BIN_PATH/psql -U repuser hotstandby -c "select * from test01"
 col01 | col02
-------+-------
     1 |
     2 |
     3 |
     4 |
     5 |
(5)

$
$ sudo /sbin/service postgresql-9.0.5433 start
$ $BIN_PATH/psql -U repuser hotstandby -p 5433 -c "select * from test01"
 col01 | col02
-------+-------
     1 |
     2 |
     3 |
     4 |
     5 |
(5)

$ 

トリガーファイルを作成し、スタンバイからマスターに昇格させる

recovery.confに設定したトリガーファイルを作成し、ログを確認してみます。また、データの更新も行えるようになることも確認します。

$ BIN_PATH=/usr/pgsql-9.0/bin/
$ PGBASE=/var/lib/pgsql/9.0
$ PORT=5433
$ touch /var/lib/pgsql/9.0/trigger_file
$ tail data_$PORT/pg_log/*
       :
LOG:  トリガファイルが見つかりました:/var/lib/pgsql/9.0/trigger_file
FATAL:  管理者コマンドにより WAL 受信プロセスを終了しています
LOG:  0/201D728のREDOが終わりました
LOG:  最後に完了したトランザクションはログ時刻2010-09-30 15:01:19.982838+09でした
cp: cannot stat `/var/lib/pgsql/9.0/backups/000000010000000000000002': そのようなファイルやディレクトリはありません
cp: cannot stat `/var/lib/pgsql/9.0/backups/00000002.history': そのようなファイルやディレクトリはありません
LOG:  選択された新しいタイムラインID: 2
cp: cannot stat `/var/lib/pgsql/9.0/backups/00000001.history': そのようなファイルやディレクトリはありません
LOG:  アーカイブリカバリが完了しました
LOG:  自動バキュームランチャプロセス
LOG:  データベースシステムの接続受付準備が整いました。
$ 
$ $BIN_PATH/psql -U repuser hotstandby -p 5433 -c "insert into test01 (col01) values (6)"
INSERT 0 1
$ $BIN_PATH/psql -U repuser hotstandby -p 5433 -c "select * from test01"
 col01 | col02
-------+-------
     1 |
     2 |
     3 |
     4 |
     5 |
     6 |
(6 行)

$ 

レプリケーション用ユーザーにパスワードを指定する場合

マスター側のpg_hba.confと、スタンバイ側のrecovery.confの設定を変更します。
マスタ側は次のように、METHODの部分を編集します。"md5"の部分は適宜。

$ BIN_PATH=/usr/pgsql-9.0/bin/
$ PGBASE=/var/lib/pgsql/9.0
$ CONF_FILE=$PGBASE/data/pg_hba.conf
$ echo "host    replication     repuser         127.0.0.1/32            md5" >> $CONF_FILE
$ 

スタンバイ側はprimary_conninfoに"password="を追加します。以下はパスワードが"foobar"の場合。

primary_conninfo = 'host=127.0.0.1 port=5432 user=repuser' password='foobar'

あとはマスター、スタンバイをそれぞれ再起動して動作を確認します。認証が失敗した場合、次のようなエラーメッセージがログに出力されます。

FATAL:  プライマリサーバへの接続ができませんでした:FATAL:  ユーザ"repuser"のパスワード認証に失敗しました

スタンバイDBを追加する

先程のスタンバイDBの構築手順で、新しいスタンバイDBを構築すればOKです。ただし、マスターDBのmax_wal_sendersの設定を超過した場合、次のようなエラーメッセージがスタンバイ側のログに出力されます。

FATAL:  プライマリサーバへの接続ができませんでした:FATAL:  要求されたスタンバイ接続が max_wal_senders を超えています(現在は 1)

別ホストにスタンバイDBを構築する

基本的な構築手順は同じですが、recovery.confのrestore_commandをrsyncやscpなどに変更する必要があります。

まとめ

最終的には、スタンバイDBを10個ぐらい立ち上げて試してみましたが、特に問題なくレプリケーションされました。使う場面があれば導入してみたい、これは。
あと、スタンバイ構築用のスクリプトを1つ書いておけば、簡単にあれこれ試せるようになるので便利です:-)