Preparing your application for PGD#

PGDの分散的な性質により、既存のアプリケーションを移植するか、スクラッチから構築するかにかかわらず、シングルノードのPostgresでは発生しないデザインの決定が発生します。このガイダンスでは、完全なデータベーススキーマとすべてのデータがすべてのノードにレプリケートされることを前提としています。

注釈

PGDは、選択的レプリケーションもサポートしているため、どのテーブルをどのノードにレプリケートするかを制御できます。 Configuring selective replication を参照してください。

ワークロードの理解#

デザインを決定する前に、アプリケーションの読み取りと書き込みの比率を理解します。読み取りを別のノードにルーティングして、負荷を分散し、書き込みリーダーノードから完全にオフロードできます。

アプリケーションがオブジェクトリレーショナルマッパーORMを使用している場合、発行されたDDLを確認して、生成されたテーブルに主キーがあることを確認し、接続マネージャーの読み取り/書き込みおよび読み取り専用ポートにマップするように個別の接続プールを構成し、生成されたSQLを監査します以下で説明するパターン。

書き込み戦略を計画する#

主な目標として、書き込み競合を回避するようにアプリケーションをデザインします。書き込みトランザクションは、クラスター内のすべてのデータノードにレプリケートされるため、競合によるコストがシングルノードデータベースよりも高くなります。

アプリケーションが、パーティション間でオーバーラップする書き込みなしで、地域またはユーザーグループごとに書き込みトラフィックをきれいにパーティション化できる場合、別のリージョンにマルチプルの書き込みリーダーを使用するローカルルーティングが最適です。書き込みをリージョンのローカルに保つことで、リージョン間の競合が排除され、スループットが向上します。そのきれいな分離が保証できない場合、書き込み量の規模が拡大するにつれて競合率が増加します。その場合、すべての書き込みが単一の書き込みリーダーに送られるグローバルルーティングの方が安全な選択です。ルーティング構成自分自身はデータベース管理者DBAのタスクですが、アプリケーションの書き込みパターンを理解することにより、どのモデルが適合するかが決まります。

PGDはデフォルトでグローバルシーケンスを使用します。 int4 およびint2 シーケンスは、ノードごとに値の範囲を割り当てるgalloc を使用します。 int8 シーケンスはsnowflakeid を使用します。どちらもIDにギャップを生成します。アプリケーションでギャップのないシーケンスを想定しないでください。シーケンス種類とそのトレードオフの詳細については、 Sequences を参照してください。競合せずにマージする必要がある可換更新の場合、 conflict-free replicated data types (CRDTs) を使用します。

競合の検出と解決の詳細については、 Conflict Management を参照してください。

読み取り戦略を計画する#

読み取りトラフィックを接続マネージャーの読み取り専用ポートにルーティングします。これにより、接続が書き込みリーダーに転送されるのではなく、すべてのノードに分散されます。 HAProxyは、Connection Managerの読み取り専用ポートを使用して、ノード全体で読み取りトラフィックのバランスを保つこともできます。構成例については、 Load Balancing with Connection Manager を参照してください。

最近行った注文や更新された口座残高など、ユーザーが自分の書き込みをすぐに確認する必要がある読み取りの場合、これらの読み取りを書き込みリーダーにルーティングするか、より厳密な コミットスコープへの移行 を使用して、完了する前に十分なノードで書き込みが可視されるようにします。集計数やソーシャルメトリックなどの古いデータが受け入れられる読み取りの場合、読み取り専用ポートを使用します。

レプリケーション用のスキーマのデザイン#

UPDATE またはDELETE 操作を行うすべてのテーブルにPRIMARY KEY またはUNIQUE 制約を定義します。 PGDは、レプリケーションの適用中の高速行ルックアップのために、制約の背後にある一意インデックスを使用します。

主キーと一意制約がないテーブルの場合、PGDは、テーブルが作成されるか、レプリケーションに追加されるときに警告を発します。 REPLICA IDENTITY FULL がデフォルトであるため、テーブルは引き続きレプリケートされます。更新と削除は、古い行全体と一致することによりレプリケートされますが、適用側のルックアップは非常に遅くなります。 PGDは、適切なインデックスが存在する場合は使用するか、シークエンシャル スキャンにフォールバックします。主キーの欠落は通常見落としであり、書き込み負荷がかかるとスキャンコストが複合します。実稼働テーブルのデフォルトに依存しないでください。

PGDは、更新可能なビューを透過的に処理します。更新可能なビューを介したデータ操作言語DMLは、オリジンの基になるベーステーブルに解決され、ターゲット上の同じベーステーブルに適用されます。

PGDは、パーティション化されたテーブルを透過的にサポートしますパーティションへの変更は、ダウンストリームに自動的にレプリケートされます。 AutoPartition in PGD は、時間の経過とともに増加する大規模なテーブルを管理するため、低競合のロックを使用してパーティションの作成と削除を自動化します。

スキーマで使用されるすべての照合順序がすべてのノードで使用できること、およびデフォルトの照合順序がノード間で同じであることを確認します。 PGDはレプリカID値で等価検索を使用するため、行レプリケーション自分自身は影響を受けません。ただし、一致しない照合修飾子で定義された一意インデックスは予期しない動作をする場合があり、照合可能な式を使用する行フィルタは、ノード間で照合が異なる場合に一貫性のない結果を生成する可能性があります。

レプリケーション動作のアカウンティング#

PGDは、すべてのトリガーとルールが処理された後、各ステートメントの最終結果のみをレプリケートします。 INSERT ... ON CONFLICT UPDATE は、オリジンで発生した挿入または更新のいずれかを送信します。更新または削除が影響を与える行がない場合、何もレプリケートされません。

次の動作はシングルノードデータベースとは異なるため、開発中に注意が必要です。

  • トリガー は、シングルノードPostgresと同様に、 SQLが最初に実行された元のノードでのみ起動します。 PGDがターゲットノードにレプリケートされた変更を適用すると、そのテーブルのトリガーは起動されません。代わりにそれらのエフェクトが複製されます。

  • ターゲットノードに変更が適用されたときにトリガーを起動するには、 ALTER TABLE ... ENABLE ALWAYS TRIGGER を使用します。適用時にのみ起動するには、 ALTER TABLE ... ENABLE REPLICA TRIGGER .

  • を使用します。ステートメントレベルのトリガー FOR EACH STATEMENT と列ごとのUPDATE トリガー UPDATE OF column_name は、設定に関係なく、適用時に実行されることはありません。

  • 適用時に起動するトリガーファンクションは、オブジェクト参照を完全修飾する必要があります、または ALTER FUNCTION ... SET search_path = ... で検索パスを明示的に設定します。 PGD applyは、システムレベルのデフォルトsearch_path を使用し、別のパスを想定するファンクションは適用時に失敗します。

  • ルールは、シングルノードPostgresと同様に、元のノードでのみ実行されます。 PGDは、ルール自分自身ではなく、結果の変更をレプリケートします。

  • SELECT ... FOR UPDATE/FOR SHARE およびアドバイザリロックからの行レベルのロックはレプリケートされません。 SERIALIZABLE 分離はサポートされていますが、トランザクションはノード間でシリアル化されず、マルチプルのノード上の同時トランザクションは引き続き競合する可能性があります。

  • バイナリデータの場合はBYTEA 列を使用します。これらは、基になるTOAST ストレージを含む最大1GBを含む完全にレプリケートされます。アプリケーションがストリーム指向のラージオブジェクトインターフェイスを必要とする場合、PGDは Replicated large objects もサポートします。

  • TOAST 対応列text 、bytea 、jsonb 、大きいvarchar などを持つテーブルでREPLICA IDENTITY FULL を使用します。 PGD 6は、BDR_AUTO レプリカIDを介して新しいテーブルにデフォルトでこれを設定します。 REPLICA IDENTITY DEFAULT またはUSING INDEX でオーバーライドすると、同時更新でデータの相違が発生するリスクがあります。 TOAST データの合計が約1GBを超える行は、この設定に関係なくレプリケートできません。

  • CHECK コンストレインでは揮発性ファンクションを回避します。 PGDは、デフォルトで適用時にCHECK 制約を再実行し、揮発性ファンクションがオリジンでの結果とは異なる結果を返す場合、レプリケーションがブレークする可能性があります。この動作は、 pgd group set-option グループオプションによって制御されます。

  • REPLICA IDENTITY 列を外部としてマークしないでください。

  • カスケードキーを含む外部キーは、書き込みノードでのみ適用され、適用時に再チェックされません。ローカルの一貫性のためにそれらを使用しますが、競合中のクロスノードの整合性違反を防止するとは考えないでください。

データ異常の回避#

PGDは、レプリカID値を使用して行を識別し、テーブル名を使用してテーブルを識別します。削除後の識別子、またはドロップ後のテーブル名を再利用すると、PGDが解決できないあいまい性が発生します。このあいまいさは ABA problem であり、PGDは、識別子が現在のオブジェクトを参照するか、以前のオブジェクトを参照するか、古いオブジェクトを参照するかを判断できません。

データの異常と相違を回避するには

  • INSERT の行に一意の識別子を使用します。

  • UPDATE の一意の識別子は変更しないでください。

  • 削除された一意の識別子は再利用しないでください。

  • ドロップされたオブジェクト名を再利用しないでください。

違反は、ノード全体でデータ異常を引き起こす可能性があります。ありそうもないことですが、不可能ではありません。削除された行の識別子は、ダウンしたノードを含むすべてのノードでDELETE が再生した後にのみ安全に再利用できます。良好な条件下では、これには1秒未満かかりますが、ノードが著しく低下している場合は数日かかる場合があります。