PostgreSQL9.1ためしてみた【非同期レプリケーション編】

前回インストールしたPostgreSQL9.1で非同期レプリケーションを試します。
資料として【PostgreSQL9.0レプリケーション・ハンズオン - PukiWik】を参考にさせて頂きましたというか、ほとんどこのとおりにやってるので、最初からこちらを見られたほうがいいと思いますw


マスター側の設定
マスターのデータディレクトリは前回同様"/usr/local/var/pgsql/data"です。

postgresql.confの設定

$ sudo vi /usr/local/var/pgsql/data/postgresql.conf
wal_level = hot_standby 
max_wal_senders = 4
wal_keep_segments = 16
  • "wal_level=host_standby" でマスタがスレーブからの接続を受け付けられるようにwad_levelにhot_standbyを指定
  • max_wal_senders でマスタが受け付けるスレーブの最大接続数を設定
  • wal_keep_segments にレプリケーション用に残しておくWALファイルの数を指定
  • wal_keep_segments の設定により最低限残しておくWALファイル数が決まるようです。なお、スレーブ側がまだ反映していない分のWALファイルが削除されてしまった場合、レプリケーションはできないようです(その場合はデータの同期からやり直しと思われます...)。なので多めにとっておいたほうが安全ですね。

あとは、今回同じホスト上でマスタとスレーブを動かすのでハンズオンの資料にならってlog_line_prefixを指定する事でログ上でマスタのログとスレーブのログが見分けがつくようにします。

log_line_prefix = '[master]'

pg_hba.confの設定
スレーブがレプリケーション用の接続をオープン出来るように接続許可を設定

$ sudo vi /usr/local/var/pgsql/data/pg_hba.conf

ファイルの末尾に以下を追加

host    replication     postgres        127.0.0.1/32            trust

マスタを起動する

$ sudo -u postgres pg_ctl -D /usr/local/var/pgsql/data start

スレーブ側用のクラスタを作る
ディレクトリは"data_slave1"とします。

最初にマスタのデータをコピーします。

コピーを開始する事をマスタに知らせる。

$ psql -U postgres -c "SELECT pg_start_backup('test')"

データをディレクトリごとコピーする。

$ cd /usr/local/var/pgsql/
$ sudo -u postgres cp -r data data_slave1

マスタにコピーの完了を知らせる。

psql -U postgres -c "SELECT pg_stop_backup()"

スレーブ側の設定
postgresql.confの設定

$ sudo vi /usr/local/var/pgsql/data_slave1/postgresql.conf
port = 5433
hot_standby = on 
  1. ポートをマスタと被らないように5432以外に変える。
  2. "hot_sandby=on"でレプリケーション中にSQLの実行を許可する

今回は同一ホスト上で動かすのでログ上で見分けがつくようにログプレフィックスを指定。通常は必要なし。

log_line_prefix = '[slave1]'

recovery,confの設定
recovery.confを設定しますが、まずはPostgreSQLのソースディレクトリからサンプルファイルをコピーしてきます。これはインストールの方法によって違う場所になりますが、今回はMacOSX上でhomebrewを使ってバージョン9.1.1を入れましたので以下のコマンドとなります。

$ sudo -u postgres cp /usr/local/Cellar/postgresql/9.1.1/share/postgresql/recovery.conf.sample /usr/local/var/pgsql/data_slave1/

コピーしてきたファイルをリネームする

$ sudo mv /usr/local/var/pgsql/data_slave1/recovery.conf.sample /usr/local/var/pgsql/data_slave1/recovery.conf

ファイルを開いて設定

sudo vi /usr/local/var/pgsql/data_slave1/recovery.conf
standby_mode = on
primary_conninfo = 'host=127.0.0.1 port=5432 user=postgres'

9.1からは以下は必須ではなくなりました。トリガファイルを使用する場合のみ以下の設定が必要です。

trigger_file = '../slave1_to_master'
  • "standby_mode = on"でスレーブとして稼動。
  • "primary_conninfo"にマスタへの接続情報を設定。
  • "trigger_file"に指定されたファイルがtouchコマンド等で作成されると、自動的にスレーブがマスタに昇格する。
  • 【注:9.1では必須ではありません。triggerファイルを使用する場合のみ設定】"trigger_file"のファイルパスはデータディレクトリ(今回は/usr/local/var/pgsql/data_slave1)からの相対ファイルパスを指定する。

スレーブを起動
起動前にコピー元にあったピッドファイルを削除

$ sudo rm data_slave1/postmaster.pid

あとは普通に起動すればいいのですが、私の環境ではsharedmemoryが足りないと叱られました。

[slave1]DETAIL:  Failed system call was shmget(key=5433001, size=3883008, 03600).
[slave1]HINT:  This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory or swap space, or exceeded your kernel's SHMALL parameter.  You can either reduce the request size or reconfigure the kernel with larger SHMALL.  To reduce the request size (currently 3883008 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.
	The PostgreSQL documentation contains more information about shared memory configuration.

したがってOS側の設定でsharedmemoryを増やしてやる必要があります。
MacOSXの場合は"/etc/sysctl.conf"に設定してやればいいです。このファイルは初期状態では存在しませんが、新規で作成すれば次回以降は起動時に読み込んでくれるようです。

sudo vi /etc/sysctl.conf

以下を追記

kern.sysv.shmmax=16777216
kern.sysv.shmall=4096

(デフォルトの4倍を割り当てました)

設定はマシンを再起動すると反映されます。

再起動後に

sysctl -A | grep shmall
sysctl -A | grep shmmax

で設定が反映されていることを確認します。

あたらめてマスターを起動

$ sudo -u postgres pg_ctl -D /usr/local/var/pgsql/data start

続いてスレーブを起動

$ sudo -u postgres pg_ctl -D /usr/local/var/pgsql/data_slave1 start

ログに
[slave1]LOG: entering standby mode
と書きだされればスレーブとして起動できています。

レプリケーションのテスト

マスタでデータベースを生成

$ createdb -U postgres -h localhost sample_db

スレーブに接続してみる

$ psql -U postgres -h localhost -p 5433 sample_db

スレーブでテーブル一覧を表示してみる

sample_db=# \d
No relations found.

テーブルは存在していない

マスタでテーブルを生成してみる

$ psql -U postgres -h localhost sample_db
create table test_tbl (id serial, name varchar(64));

スレーブで再びテーブル一覧を表示してみる

sample_db=# \d
               List of relations
 Schema |      Name       |   Type   |  Owner   
--------+-----------------+----------+----------
 public | test_tbl        | table    | postgres
 public | test_tbl_id_seq | sequence | postgres
(2 rows)

スレーブ側でもテーブルが生成されました。

マスタで値をinsertしてみる

insert into test_tbl (name) values('hoge');

スレーブで参照してみる

select * from test_tbl;
 id | name 
----+------
  1 | hoge
(1 row)

データがレプリケーションされました。

スレーブ側でupdateができないことを確認

update test_tbl set name = 'hoge2';
ERROR:  cannot execute UPDATE in a read-only transaction

スレーブ停止中のマスタへの更新が反映されるか確認
スレーブを停止

$ sudo -u postgres pg_ctl -D /usr/local/var/pgsql/data_slave1 stop

マスタ側でデータをinsert

insert into test_tbl (name) values('now_slave_stopping');

再びスレーブを起動

$ sudo -u postgres pg_ctl -D /usr/local/var/pgsql/data_slave1 start

スレーブに接続してselectを発行

$ psql -U postgres -h localhost -p 5433 sample_db
select * from test_tbl;
 id |        name        
----+--------------------
  1 | hoge
  2 | now_slave_stopping
(2 rows)

ちゃんと反映されてました(^O^)

フェイルオーバー
マスタが停止した時に、それまでスレーブとして稼働していたプロセスをノンストップでマスタに昇格します。
確認のため、マスタを停止させる

$ sudo -u postgres pg_ctl -D /usr/local/var/pgsql/data stop

ここで今までスレーブとして動作していたクラスタをマスタに昇格しますが、やり方は2つあります。
一つは上記のスレーブの設定の所のtrigger_fileで設定したファイルをtouchコマンドなどで生成する方法。
もうひとつは9.1から実装された方法で、"pg_ctl promote -D データディレクトリ"というコマンドによりマスターへの昇格を指示する方法です。

pg_ctl promoteコマンドを使ってフェイルオーバー
9.0であれば選択の余地はなくtrigger_fileを使った方法ですが、9.1では後から紹介したコマンドによる方法の方がスマートだと思います。
今回の構成では以下のようにコマンドを入力します。

$ sudo -u postgres pg_ctl promote -D /usr/local/var/pgsql/data_slave1

trigger_fileを使ってフェイルオーバー
スレーブ側のrecovery.conf内で"trigger_file"に設定したファイルをtouchコマンドで生成する。

$ sudo -u postgres touch /usr/local/var/pgsql/slave1_to_master


いずれの方法でもスレーブのログにマスターへ昇格した旨が書き出されます。

[slave1]LOG:  received promote request
または
[slave1]LOG:  trigger file found: ../slave1_to_master
...
[slave1]LOG:  redo done at 0/4016B48
[slave1]LOG:  last completed transaction was at log time 2011-11-13 19:16:29.851876+09
[slave1]LOG:  selected new timeline ID: 2
[slave1]LOG:  archive recovery complete
[slave1]LOG:  database system is ready to accept connections
[slave1]LOG:  autovacuum launcher started

昇格した新しいマスタに接続する

$ psql -U postgres -h localhost -p 5433 sample_db

先ほどスレーブとして稼働していた際に失敗した更新系SQLを再度ためしてみる。

update test_tbl set name = 'hoge2';
UPDATE 2

成功した模様。データを確認

select * from test_tbl;
 id | name  
----+-------
  1 | hoge2
  2 | hoge2
(2 rows)

データが更新されたので、狙い通りマスタとして稼動しているようです(´ー`)

次回は同期レプリケーション