レプリケーションの諸機能と、同期接続

2011年10月27日(木)
那賀 樹一郎(なか きいちろう)

同期レプリケーションへの変更

まずは、前回までの非同期レプリケーションの処理の流れの概要を見てみます。

図2:非同期での処理の流れ

実行の順序を見ていただくと分かる通り、プライマリがローカルのディスクへ WAL を書き出し終えたところで、クライアントへはコミットの成功を返し、その後で、クライアントの操作とは全く非同期に、スタンバイ側への WAL レコードの転送とディスクへの書き出しを行います。

それに対して、同期レプリケーションの処理は以下のようになります。

図3:同期での処理の流れ

プライマリのディスクに WAL を書き出したことを確認した後で、スタンバイへ WAL ログを送信します。そしてスタンバイも WAL をディスクへ書き出したことを確認できたら、ようやくクライアントへコミットの成功を返します。パフォーマンスと引き換えに、データの保護を優先した構成と言うことができます。

それでは、前回の非同期レプリケーションの構成を、そのまま同期に変更して行きましょう。~postgres/pgdata-prim/postgresql.conf を下記のように修正し、同期スタンバイになるノードの application_name を指定します。

synchronous_standby_names = ''

  ↓

synchronous_standby_names = 'stby'

修正し終えたら、プライマリ、スタンバイ共に再起動します。

-bash-4.1$ pg_ctl -D ~/pgdata-prim restart
サーバ停止処理の完了を待っています....完了
サーバは停止しました
サーバは起動中です。
-bash-4.1$ pg_ctl -D ~/pgdata-stby/ restart
サーバ停止処理の完了を待っています....完了
サーバは停止しました
サーバは起動中です。
-bash-4.1$ psql -x -p 5432 -c "SELECT * FROM pg_stat_replication"
-[ RECORD 1 ]----+------------------------------
procpid          | 13136
usesysid         | 16384
usename          | reprole
application_name | stby
client_addr      | 127.0.0.1
client_hostname  |
client_port      | 42722
backend_start    | 2011-XX-XX 16:58:58.420314+09
state            | streaming
sent_location    | 0/3037000
write_location   | 0/3037000
flush_location   | 0/3037000
replay_location  | 0/3036FA8
sync_priority    | 1
sync_state       | sync

-bash-4.1$

sync_priority が "0" から "1" へ、sync_state が "async" から "sync" へと、同期でのレプリケーションを表す値に変わったのが分かるかと思います。試しに、同期スタンバイを停止させてからプライマリに書き込みを行うとどうなるかを見てみましょう。

-bash-4.1$ pg_ctl -D ~/pgdata-stby/ stop
サーバ停止処理の完了を待っています....完了
サーバは停止しました
-bash-4.1$ psql -p 5432 -c "INSERT INTO members VALUES(128, 'Hoge')" # 戻らない…
...

このように、スタンバイ側への書き込みが確認されるまで、プライマリへの変更は確定しません。別のコンソールからスタンバイ側のノードをスタートさせれば、上記のクエリは成功します。

メンテナンスなどのためにプライマリを単体で起動させたい場合には、一時的に synchronous_commit パラメータを "local" にすることで、スタンバイへの同期レプリケーションができなくても処理が返るようになります。

-bash-4.1$ pg_ctl -D ~/pgdata-stby/ stop # スタンバイの停止
サーバ停止処理の完了を待っています....完了
サーバは停止しました
-bash-4.1$ pg_ctl -D ~/pgdata-prim/ -o "-c synchronous_commit=local" restart
サーバ停止処理の完了を待っています....完了
サーバは停止しました
サーバは起動中です。
-bash-4.1$ psql -p 5432 -c "INSERT INTO members VALUES(256, 'Fuga')" # 戻る
INSERT 0 1
-bash-4.1$ pg_ctl -D ~/pgdata-prim/ -o "-c synchronous_commit=on" restart
サーバ停止処理の完了を待っています....完了
サーバは停止しました
サーバは起動中です。
-bash-4.1$ psql -p 5432 -c "INSERT INTO members VALUES(512, 'Hare')" # 戻らない…

最後に、プライマリノードを終了手続きを経ずに異常終了させ、それまでのスタンバイを新プライマリに昇格させてみます。その後で、旧プライマリを新スタンバイとして、pg_basebackup コマンドによる再構築なしに、recovery_target_timeline='latest' の設定を用いることで、新プライマリに再度接続させてみます。この際、~/pgdata-stby/pg_log/ ディレクトリに出力されているテキストログを見ながら作業をすると、昇格の流れが分かりやすいかと思います。

-bash-4.1$ pg_ctl -D ~/pgdata-prim/ -m immediate stop
サーバ停止処理の完了を待っています...完了
サーバは停止しました
-bash-4.1$ pg_ctl -D ~/pgdata-stby/ promote
サーバを昇進中です。
-bash-4.1$

旧プライマリを新スタンバイにするために、~postgres/pgdata-prim/recovery.conf を準備します。

standby_mode = 'on'
primary_conninfo = 'host=localhost port=5433 user=reprole password=reppass application_name=prim'
recovery_target_timeline = 'latest'
restore_command = 'cp /var/lib/pgsql/pgdata-stby/pg_xlog/%f "%p" 2> /dev/null'

新スタンバイのサービスを起動します。

-bash-4.1$ pg_ctl -D ~/pgdata-prim/ start
サーバは起動中です。
-bash-4.1$

レプリケーションが行われていることを確認します。

-bash-4.1$ psql -p 5433 -c "INSERT INTO members VALUES(777, 'Foo')"
INSERT 0 1
-bash-4.1$ psql -p 5433 -c "SELECT * FROM members WHERE id = 777" # 新プライマリ
 id  | name
-----+------
 777 | Foo
(1 行)

-bash-4.1$ psql -p 5432 -c "SELECT * FROM members WHERE id = 777" # 新スタンバイ
 id  | name
-----+------
 777 | Foo
(1 行)

-bash-4.1$

次回は、Keepalived の VRRP サービスを利用して、非同期(2 台構成)と同期(3 台構成)のそれぞれによる簡単な HA (High-Availability: 高可用性) システムの構成を行う予定です。

【関連リンク】

<サイト最終アクセス:2010.10>

著者
那賀 樹一郎(なか きいちろう)

サイオステクノロジー株式会社にて、PostgreSQL/Postgres Plus の製品担当として、サポートサービス、コンサルティング、トレーニング等を行う。Linux ディストリビューションベンダーで培った、OSS プロダクトのソースコードレベルでのソフトウェア解析技術を生かして業務にあたる。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています