本文へ移動

レプリケーションモード

Patroniが管理する非同期・同期レプリケーションモード。

PatroniはPostgreSQLのストリーミングレプリケーションを使用します。ストリーミングレプリケーションの詳細はPostgresのドキュメント を参照してください。デフォルトでは、PatroniはPostgreSQLに非同期レプリケーションを設定します。レプリケーション方式の選択は業務上の要件によって異なります。非同期と同期の両方式、および他の高可用性ソリューションを検討し、最適なものを判断してください。


非同期モードの永続性

非同期モードでは、可用性を確保するために、コミット済みトランザクションの一部が失われることを許容します。プライマリーサーバーに障害が発生した場合や、その他の理由で利用不能になった場合、Patroniは十分に正常なスタンバイを自動的にプライマリーへ昇格させます。そのスタンバイにレプリケーションされていないトランザクションは、プライマリー上の「分岐したタイムライン」に残り、事実上リカバリーできません1。

失われる可能性があるトランザクション量は、maximum_lag_on_failoverパラメーターで制御します。プライマリーのトランザクションログ位置をリアルタイムには取得しないため、フェイルオーバー時に失われるデータ量の実際の最悪値は、maximum_lag_on_failoverバイトのトランザクションログに、直近のttl秒間(平均ではloop_wait/2秒間)に書き込まれた量を加えたものです。ただし、通常の定常状態でのレプリケーション遅延は1秒を大幅に下回ります。

デフォルトでは、Patroniはリーダー選出時にレプリカの現在のタイムラインを考慮しません。これは状況によっては望ましくない動作です。check_timelineをtrueに設定すると、タイムラインがクラスターで最後に確認されたものより古いノードや、タイムラインを特定できないノードが新しいリーダーになることを防げます。


PostgreSQLの同期レプリケーション

Patroniでは、Postgresの同期レプリケーション を使用できます。同期レプリケーションでは、接続元のクライアントに成功を返す前に、書き込みがセカンダリーに保存されたことを確認し、クラスター全体の整合性を確保します。その代償として、書き込みのレイテンシーが増加し、スループットが低下します。このスループットはネットワーク性能の影響を受けます。

ホスティング型のデータセンター環境(AWS、Rackspaceなど、または自分で制御できないネットワーク)では、同期レプリケーションにより書き込み性能の変動が大幅に増加します。リーダーからフォロワーに到達できなくなると、リーダーは実質的に読み取り専用になります。

簡単な同期レプリケーションのテストを有効にするには、YAML設定ファイルのparametersセクションに次の行を追加します。

synchronous_commit: "on"
synchronous_standby_names: "*"

PostgreSQLの同期レプリケーションを使用する場合は、ホスト1台が故障しても書き込み可用性を確保できるよう、少なくとも3つのPostgresデータノードを使用してください。

PostgreSQLの同期レプリケーションを使用しても、あらゆる状況でトランザクションが一切失われないことを保証するわけではありません。プライマリーと現在同期レプリカとして動作しているセカンダリーが同時に故障すると、すべてのトランザクションを保持しているとは限らない3つ目のノードが昇格します。


同期モード

コミット済みトランザクションの損失を許容できない場合は、Patroniのsynchronous_mode を有効にできます。synchronous_mode を有効にすると、クライアントにコミット成功を返した可能性があるすべてのトランザクションをスタンバイが保持していると確信できない限り、Patroniはそのスタンバイを昇格させません2。つまり、一部のサーバーが利用可能でも、システムへの書き込みができない場合があります。システム管理者は、トランザクションが失われる場合でも、手動フェイルオーバーコマンドでスタンバイを昇格させることができます。

synchronous_mode を有効にしても、あらゆる状況で複数ノードにコミットが永続化されることを保証するわけではありません。適切なスタンバイが利用できない場合も、プライマリーは書き込みを受け付けますが、そのレプリケーションは保証しません。このモードでプライマリーが故障すると、スタンバイは昇格しません。以前のプライマリーのホストが復帰すると、管理者が手動フェイルオーバーを行っていない限り、自動的に昇格します。この動作により、2ノードのクラスターでも同期モードを使用できます。

synchronous_mode が有効で厳密モードが無効な状態で、唯一の同期スタンバイがクラッシュすると、Patroniの次の処理サイクルでプライマリーを単独動作モードへ切り替えるまで、コミットはブロックされます。書き込みの遅延は最悪でttl秒、平均でloop_wait/2秒です。スタンバイを手動で停止または再起動する場合は、コミット処理が中断されません。PostgreSQLの停止を開始する前に、スタンバイは同期スタンバイの役割から自身を外すようプライマリーに通知します。

各書き込みが少なくとも2つのノードに永続保存されることを必ず保証する必要がある場合は、synchronous_mode に加えてsynchronous_mode_strictを有効にしてください。このパラメーターは、同期スタンバイの候補が存在しない場合にPatroniがプライマリーの同期レプリケーションを無効にすることを防ぎます。同期コミットは同期レプリカが利用可能になるまでブロックされます。synchronous_commitがoffまたはlocalのトランザクションは、この待機を回避します。

優先順位に基づく同期モード(synchronous_mode: on)で厳密モードが有効であり、適切な同期スタンバイが利用できない場合、Patroni 4.1.5はsynchronous_standby_namesを*に設定し、警告"No standbys available!"を出力します。これにより、コミットは同期スタンバイを待ち続けます。このワイルドカードは、確認応答の送信元を/syncに以前記録されていたメンバー名に限定しません。

nosyncタグをtrueに設定すると、Patroniはそのスタンバイを同期スタンバイの選択対象から除外します。低速なネットワーク経由で接続されており、同期スタンバイになると性能が低下するスタンバイには、この設定を推奨します。nostreamタグをtrueに設定しても同じ効果があります。

同期モードは、patronictl edit-configコマンドまたはPatroniのRESTインターフェースで有効・無効を切り替えられます。手順は動的設定 を参照してください。

注意:PostgreSQLの同期レプリケーションの実装上、synchronous_mode_strictを使用していてもトランザクションが失われる可能性があります。クライアントのタイムアウトによるキャンセルパケットやバックエンド障害により、レプリケーションの確認を待っているPostgreSQLバックエンドがキャンセルされると、そのトランザクションの変更は他のバックエンドから見えるようになります。これらの変更はまだレプリケーションされていないため、スタンバイが昇格すると失われる可能性があります。


同期レプリケーション係数

Patroniはsynchronous_node_countパラメーターで同期スタンバイデータベースの数を管理します。デフォルトは1です。synchronous_mode がoffの場合、このパラメーターは効果を持ちません。有効な場合は、Patroniがsynchronous_node_countに基づいて同期スタンバイデータベースの正確な数を管理し、メンバーの参加・離脱に合わせてDCSの状態とPostgreSQLのsynchronous_standby_namesを調整します。適格なノードが不足している場合、Patroniが使用する同期スタンバイは要求された数より少なくなることがあります。ただし、厳密なクォーラムモードでは、コミットは設定された数のスタンバイからの確認応答を待ち続けます。


同期ノードの最大遅延

デフォルトでは、Patroniは他のノードの方が先に進んでいても、pg_stat_replicationビューでsynchronousとされているノードを維持します。これはsynchronous_standby_namesの変更回数を最小限にするためです。この動作を変更するには、maximum_lag_on_syncnodeパラメーターを使用できます。レプリカを「同期」とみなせる遅延量の上限を制御します。

スタンバイが複数ある場合、Patroniはレプリカの最大LSNを使用します。それ以外の場合は、リーダーの現在のWAL LSNを使用します。デフォルトは-1で、値が0以下の場合、Patroniは正常でない同期スタンバイを入れ替えません。トランザクション量が多いときに同期スタンバイが頻繁に入れ替わらないよう、十分大きな値を設定してください。


同期モードの実装

同期モードでは、PatroniはDCSの/syncキーに、最新のプライマリーと現在の同期スタンバイデータベースを含む同期状態を保持します。この状態を厳密な順序制約に従って更新し、次の不変条件を保証します。

  • 書き込みトランザクションを受け付けられるノードは、常に最新のリーダーとして記録されていなければなりません。PatroniのクラッシュやPostgreSQLが停止しない状況は、この不変条件に違反する原因になります。
  • DCSの/syncキーに同期スタンバイとして公開されている限り、そのノードはPostgreSQLでも同期スタンバイに設定されていなければなりません。
  • リーダーでも現在の同期スタンバイでもないノードは、自動的に自身を昇格させることができません。

同期スタンバイの集合が空でない場合、Patroniはsynchronous_node_countに基づき、1つ以上の同期スタンバイノードをsynchronous_standby_namesに設定します。

PatroniはHAループの反復ごとに同期スタンバイの選択を見直します。現在の同期スタンバイが接続されており、同期状態の解除を要求していなければ、その選択を維持します。それ以外の場合は、同期可能なクラスターメンバーのうち、レプリケーションが最も先に進んでいるものを選びます。

例:

DCSの/configキー

synchronous_mode: on
synchronous_node_count: 2
...

DCSの/syncキー

{
    "leader": "node0",
    "sync_standby": "node1,node2"
}

postgresql.conf

synchronous_standby_names = 'FIRST 2 (node1,node2)'

上記の例では、同期していると確認されているのはnode1とnode2だけであり、プライマリー(node0)が故障した場合に自動昇格が許可されるのも、これらのノードだけです。


クォーラムコミットモード

PostgreSQL v10以降、Patroniはクォーラムに基づく同期レプリケーションをサポートしています。

このモードでは、Patroniは最後に確認されたプライマリー、クォーラムに必要なノード数、現在クォーラムへの投票資格があるノードを含む同期状態をDCSに保持します。定常状態では、投票ノードはリーダーとすべての同期スタンバイです。この状態を、ノードの昇格とsynchronous_standby_namesに関する厳密な順序制約に従って更新します。その結果、クォーラムを構成できる投票ノードのどの部分集合にも、最後に成功したコミットを保持するノードが少なくとも1つ含まれることを常に保証します。

PatroniはHAループの反復ごとに、ノードの可用性と要求されたクラスター設定に基づいて、同期スタンバイとクォーラムの選択を見直します。PostgreSQL 9.6より新しいバージョンでは、適格なすべてのノードを、レプリケーションがリーダーに追いつき次第、同期スタンバイに追加します。

クォーラムコミットでは、あるスタンバイへのレプリケーション遅延が増えても、他のスタンバイが補えるため、通常の運用中も含めて最悪時のレイテンシーを減らせます。

クォーラムに基づく同期モードを有効にするには、patronictl edit-configコマンドまたはPatroniのRESTインターフェースで、synchronous_mode をquorumに設定します。手順は動的設定 を参照してください。

synchronous_node_count、maximum_lag_on_syncnode、synchronous_mode_strictなどの他のパラメーターは、synchronous_mode=onの場合と同様に機能します。

クォーラムコミットモードで同期スタンバイの集合が空であり、厳密モードでN台のスタンバイからの確認応答が必要な場合、Patroni 4.1.5はsynchronous_standby_namesをANY N (*)に設定します。

例:

DCSの/configキー

synchronous_mode: quorum
synchronous_node_count: 2
...

DCSの/syncキー

{
    "leader": "node0",
    "sync_standby": "node1,node2,node3",
    "quorum": 1
}

postgresql.conf

synchronous_standby_names = 'ANY 2 (node1,node2,node3)'

上記の例でプライマリー(node0)が故障した場合、node1、node2、node3のうち2つは最新のトランザクションを受信していますが、それがどのノードかは分かりません。node1が最新のトランザクションを受信しているかを判断するには、node2とnode3のうち少なくとも1つ(/syncキーのquorum=1)のLSNと比較する必要があります。そのうち少なくとも1つよりnode1が遅れていなければ、node1を昇格してもユーザーに見えるデータ損失が生じないことを保証できます。


  1. データ自体は残っていますが、復元にはデータリカバリーの専門家による手作業が必要です。use_pg_rewindで巻き戻しを許可している場合、故障したプライマリーをクラスターに再参加させるために、分岐したタイムラインは自動的に消去されます。ただし、use_pg_rewindが正しく動作するには、data page checksums(initdbの--data-checksumsオプション)を有効にしてクラスターを初期化するか、wal_log_hintsをonに設定するか、その両方が必要です。 ↩︎

  2. クライアントは、PostgreSQLのsynchronous_commit設定でトランザクションごとに動作を変更できます。synchronous_commitがoffまたはlocalのトランザクションは、フェイルオーバーで失われる可能性がありますが、レプリケーション遅延によってブロックされません。 ↩︎