2011/12/08

PostgreSQL レプリケーションの Q&A PostgreSQL Advent Calendar #8

この記事は、PostgreSQL Advent Calendar の 12/8 担当分です。

只今 12/8 21 時。12/8 も残り 3 時間というところで何もいいネタが思い浮かばなかったので、とりあえずレプリケーション関係について Q&A を殴り書きしていきます。

  • Q. PostgreSQL 内蔵のレプリケーションってある?
  • A. あります。バージョン 9.0 から使えます。
  • Q. レプリケーションの同期モードは?
  • A. v9.0 は非同期のみ、v9.1 以降は同期と非同期を選択できます。
  • Q. スタンバイでは SQL を実行できる?
  • A. 参照形の SQL だけですが、スタンバイで実行できます。実行できる/できない SQL は、マニュアルに記載されています。
  • Q. どうやってフェイルオーバさせるの?
  • A. スタンバイで、trigger_file というパラメータに設定したファイルを作成することで、スタンバイをマスタに切り替えることができます。v9.1 であれば、trigger_file の作成以外に、pg_ctl という監視コマンドに対して promote というサブコマンドを指定して実行するだけで切り替えることができます。  
  • Q. マスタが故障したときに自動的にフェイルオーバさせるとかできる?
  • A. PostgreSQL だけではできません。pgpool-II や Pacemaker などのクラスタ製品と組み合わせる必要があります。
  • Q. Pacemaker と組み合わせることってできるの?
  • A. PostgreSQL のレプリケーションを制御するための RA (リソースエージェント) を作成しないと組み合わせられません。ただ、Linux-HA Japan のコミュニティで、そんなような RA を作る動きがあります。 
  • Q. 一部のテーブルだけレプリケーションさせるってできる?
  • A. できません。データベース全体がレプリケーション対象になります。ただ、v9.1 では UNLOGGED TABLE という WAL を発生させないテーブルを定義でき、このテーブルはレプリケーションされません。UNLOGGED TABLE のテーブルは WAL を一切発生させないため、PostgreSQL がクラッシュしたとき、リカバリをしてもデータは復元できません。クラッシュ時にデータが失われてもいいから、レプリケーションさせたくないといったテー ブルがあれば、UNLOGGED TABLE を使えます。
  • Q. 同期レプリケーションってどこまでデータを保証するの?
  • A. マスタで実行されたトランザクションは、そのトランザクションの WAL (トランザクションログ) がスタンバイのディスクに同期書き込み (fsync) されるまで待ちます。つまり、クライアントから見ると、トランザクションのコミットの成功応答を受け取ったとき、マスタとスタンバイの両方のディスクに WAL が書き込まれていることを保証できます。WAL がスタンバイのディスクに書き込まれているということは、マスタが故障しても、スタンバイにフェイルオーバすることで、コミットされたデータを見ることが できます。
  • Q. 同期レプリケーションで、コミットされたデータをすぐにスタンバイで見られる?
  • A. 基本的にはすぐに見られます。けど、見られないこともあります。↑で回答しているように、同期レプリケーションでは、コミットが完了した時点で、そのトラ ンザクションの WAL がスタンバイのディスクに書き込まれていることまでしか保証しません。その WAL がリカバリされ、スタンバイで参照 SQL を実行して見られるようになっているかどうかまでは責任を持ちません。ただし、ディスクに書き込まれた WAL は速やかにリカバリされるので、基本的にはすぐに参照 SQL を投げてもスタンバイからデータを見られます。
  • Q. WAL がリカバリされるまで待つ同期モードって作らないの?
  • A. v9.1 の開発途中にはそんなような話もありました。けど、まずは一番シンプルな (WAL を同期書き込みするまで待つ) 同期モードを開発して、徐々に複雑なモードを入れていこうということで、開発は一旦保留になっています。
  • Q. スタンバイが WAL を同期書き込みまでじゃなくて、受信するまでトランザクションを待たせるっていう同期モードを作ったら、同期レプリケーションのオーバーヘッドって下がるのでは?
  • A. たぶん下がります。そんなパッチのプロトタイプは作成済で、後は完成させて性能検証してコミュニティに投稿するだけなのですが、時間がありません。。。。
  • Q. 一部のトランザクションだけ同期レプリケーションさせるってできる?
  • A. synchronous_commit というパラメータを使ってできます。synchronous_commit は、トランザクションごとに設定変更できるパラメータです。synchronous_commit が on に設定されている場合、トランザクションは、マスタとスタンバイに WAL が同期書き込みされるまでトランザクションを完了させません。設定値を local にすると、マスタに WAL が同期書き込みされた時点でトランザクションは完了となり、レプリケーションを待ちません。設定値を off にすると、トランザクションはマスタとスタンバイの両方について WAL の同期書き込みを待ちません。postgresql.conf では、synchronous_commit を local に設定しておいて、同期レプリケーションしたいトランザクションについてだけ SET synchronous_commit TO on と設定した上で実行することで、そのトランザクションだけレプリケーションを待つようにできます。
  • Q. カスケードレプリケーションってできる?
  • A. v9.1 以前ではできません。来年夏頃リリース予定の v9.2 から使える予定です。
  • Q. レプリケーションってどんな経路でデータがマスタからスタンバイに伝わるの?
  • A. 次のような経路です。 
    • 1. バックエンド (SQL を実行するプロセス) が、トランザクションをコミットするときに、そのトランザクションの WAL をマスタのディスクに同期書き込みする 
    • 2. バックエンドは、walsender (WAL をマスタからスタンバイに送信するプロセス) に WAL の転送を要求する ・非同期レプリケーションだと、この時点でトランザクションは完了し、コミットの成功応答がクライアントに返る。以降の WAL の転送などは、トランザクションとは独立に実行される ・同期レプリケーションだと、スタンバイから応答があるまでレプリケーション待ちの状態になる
    • 3. 要求を受けて、walsender は WAL をディスクから読み込み、WAL をスタンバイに送信する
    • 4. スタンバイにおいて、マスタから送信された WAL を walreceiver (WAL を受信するプロセス) が受信する
    • 5. walreceiver は、受信した WAL をディスクに同期書き込みする
    • 6. walreceiver は、startup (WAL をリカバリするプロセス) に WAL のリカバリを要求する
    • 7. 要求を受けて、startup は WAL をディスクから読み込み、リカバリする (7 の実行順序は 8 や 9 の後になることもある)
    • 8. walreceiver は、どこまで WAL を書き込んだのか応答をマスタに返す
    • 9. マスタにおいて、スタンバイから返された応答を walsender が受信する ・同期レプリケーションだと、応答を受けて、walsender はレプリケーション待ちのトランザクションを再開させ、完了させる
  • Q. WAL を送信するたびに walsender が WAL をディスクから読み込んでるけど、I/O 負荷高くならない?
  • A. あまり高くなりません。基本的に walsender が読み込むのは、バックエンドが書き込んだばかりの WAL です。そのような WAL はデータがファイルキャッシュに残っているため、読み込んでも I/O 負荷はそれほど高くなりません。ただし、古いデータのスタンバイをマスタに組み込むときなど、ファイルキャッシュから追い出されているような古い WAL を転送する必要がある状況では I/O 負荷は増えてしまいます。
  • Q. 同期レプリケーションで、スタンバイが故障したらどうなる?
  • A. マスタで実行中のトランザクションは停止します。同期レプリケーションは、マスタとスタンバイの両方に WAL を書き込むまでトランザクションを完了させないことを確実に保証しています。この保証を遵守するために、スタンバイが故障して WAL を書き込めない状況でも、スタンバイに WAL を書き込めるまでトランザクションを待たせます。つまり、新しいスタンバイが現れて、WAL を書き込めるようになるまでトランザクションは待たされます。
  • Q. それだと、ノード2台で可用性構成を組んでるとき、ノード1台が故障しただけで処理が止まって、可用性下がるんじゃ?
  • A. はい、そのような状況だと可用性は下がります。ノード1台でも処理を続行したいのであれば、一時的にレプリケーションを非同期モードに設定する必要があり ます。pgpool-II や Pacemaker のクラスタ製品と組み合わせて使っているのであれば、スタンバイ切り離し時やフェイルオーバ時の動作として、同期モードを変更するように作り込んでおけ ば、故障発生時でもノード1台でスムーズにトランザクションを続行できそうです。
  • Q. なんで↑みたいな仕様にしたの?
  • A. 実はこの仕様については v9.1 の開発で散々どうすべきかコミュニティ内で揉めました。結局、最も信頼性の高い仕様でまずは同期レプリケーションをリリースして、より柔軟なものは後で 徐々に入れていこうという話になり、↑の仕様となりました。ノード1台で処理を続行させる仕様だと、その1台が故障したときに完全にコミットされたデータ が失われてしまうリスクがあります。このデータ損失のリスクと、可用性が下がるリスクを天秤にかけたとき、データ損失の方がリスクが高いということにな り、データ損失を回避する信頼性の高い仕様がまずは選ばれたということになります。柔軟な動作モードについては、v9.2 以降に期待してください。
  • Q. スタンバイで SQL 実行したとき、リカバリと競合することがあるって聞いたけど? 
  • A. はい、スタンバイでは SQL 実行とリカバリが競合することがあります。競合が発生すると、SQL が終わるまでリカバリが止まってしまったり、競合した SQL が途中でキャンセルされたりします。競合の詳細は、マニュアルを参照してください。
  • Q. 競合を回避するには?
  • A. 競合しないように PostgreSQL のパラメータを設定します。v9.0 では設定が非常に難しいのですが、v9.1 では簡単で hot_standby_feedback というパラメータをスタンバイ側で有効にするだけです。ただし、この設定では1つ注意があります。スタンバイでロングトランザクション (開始されたまま長時間完了しないままとなっているトランザクション、SQL) が発生すると、その間、マスタで VACUUM が実行されても不要領域が回収されなくなります。スタンバイでロングトランザクションが発生しないように注意しましょう。
  • Q. スタンバイがどこまで WAL を受信した (ディスクに同期書き込みした) か知りたい 
  • A. スタンバイで pg_last_xlog_receive_location() 関数を呼んでください。v9.1 だと、マスタの pg_stat_replication ビューの flush_location 列からも分かります。
  • Q. スタンバイがどこまで WAL をリカバリしたか知りたい
  • A. スタンバイで pg_last_xlog_replay_location() 関数を呼んでください。v9.1 だと、マスタの pg_stat_replication ビューの replay_location 列からも分かります。ただし、replay_location だけは情報が古い (PostgreSQL のパラメータ wal_receiver_status_interval の設定時間だけ古くなる) ため注意してください。あと、v9.1 だと、pg_last_xact_replay_timestamp() という関数を呼んで、どの時刻のトランザクションまでリカバリされたか知ることができます。
  • Q. ↑の関数やビューで表示される文字列の意味が分からない
  • A. その文字列は、WAL のバイト位置を "8桁16進数/8桁16進数" という形式で表した LSN というものです。LSN だとよく分からないという人は、マスタで、LSN を引数に pg_xlogfile_name() という関数を呼んで、LSN を WAL ファイル名に変換しましょう。 LSN 同士の引き算については、ここの記事が参考になります。
  • Q. スタンバイでリカバリを一時停止できる?
  • A. できます。pg_xlog_replay_pause() を呼んででリカバリを一時停止したり、pg_xlog_replay_resume() を呼んでリカバリを再開したりできます。
  • Q. フェイルオーバ後、故障したノードをどうやってレプリケーションに復帰させるの? 
  • A. 新しいマスタからオンライン物理バックアップを取得・展開して、スタンバイとしての設定をした上でノードを起動します。そうすると、自動的にスタンバイはマスタに接続し、レプリケーションが再開されます。
  • Q. 復帰させるのにバックアップの取得って必須なの? 面倒。。
  • A. 必須です。バックアップの取得は確かに v9.0 以前では面倒ですが、v9.1 だと pg_basebackup というコマンド一発で簡単に取得できます。 
  • Q. けど、データベースサイズが大きいからバックアップしたくない
  • A. rsync で差分バックアップしましょう。
  • Q. レプリケーション中にスタンバイが落ちたとき、そのスタンバイをどう復帰させる? 
  • A. 基本的には、その落ちたスタンバイを再起動するだけです。自動的に前回受信したところからレプリケーションが再開されます。ただし、スタンバイとマスタの 差が非常に大きい場合、スタンバイが必要とする WAL ファイルがマスタから既に消えている可能性があります。この場合は、マスタからオンライン物理バックアップを取得・展開して、スタンバイを再構成しなけれ ばなりません。
今日のところは、この辺で Q&A 打ち止めにします。また、Q&A の要望があれば、書き足していきたいと思います。。。

明日の PostgreSQL Advent Calendar 担当は、高津英輔さんです。よろしく~