Overview#

EDB Postgres Distributedは、アクティブ/アクティブまたはマルチマスターDBMSです。非同期で使用される場合、標準のデータ型を使用するときに、複数の異なるノードから同じまたは関連する行への書き込みにより、データの競合が発生する可能性があります。

競合はエラーではありません。ほとんどの場合、これらは発生時にPGDが検出および解決できるイベントです。それらの解決は、アプリケーションの性質とデータの意味によって異なります。したがって、PGDはアプリケーションに競合を解決する方法についての幅広い選択肢を提供することが重要です。

デフォルトでは、競合は行レベルで解決されます。 2つのノードからの変更が競合する場合、 PGDはローカルまたはリモートタプルのいずれかを選択し、他方は破棄します。たとえば、コミットタイムスタンプは、2つの競合する変更と新しい方が保持されると比較される場合があります。このアプローチでは、すべてのノードが同じ結果に収束し、クラスター全体でコミットオーダーのようなセマンティクスを確立します。

欠落列の競合の解決 で説明しているように、競合処理は構成可能です。 PGDは、 Stream triggers で説明しているように、競合トリガーを使用してテーブルごとに競合を検出し、それらをさまざまな方法で処理できます。

CLCD で説明しているように、列レベルの競合の検出と解決は、PGDで利用できます。

デフォルトでは、すべての競合は bdr.conflict_history にログに記録されます。競合の可能性がある場合、テーブル所有者は競合を監視して回避方法を分析するか、アプリケーションタスクとして定期的に処理する計画を立てる必要があります。 LiveCompare ツールは、発散がないか定期的にスキャンすることもできます。

一部のクラスタリングシステムは、分散ロックメカニズムを使用して、データへの同時アクセスを防止します。これらはサーバーが互いに非常に近い場合に合理的なパフォーマンスを発揮しますが、許容可能なパフォーマンスには非常に低い遅延が重要である地理的に分散したアプリケーションをサポートできません。

分散ロックは本質的に悲観的なアプローチです。 PGDは、可能な限り競合を回避するが、一部の種類の競合の発生を許可し、発生した場合にそれらを解決する楽観的なアプローチを提唱しています。

競合が発生する仕組み#

ノード間の競合は、関係するすべてのトランザクションが同じノードで同時に発生した場合に発生することはできない一連のイベントの結果として発生します。ノードはトランザクションがコミットした後にのみ変更を交換するため、各トランザクションはコミットしたノードで個別に有効です。他の競合する作業を同時に実行した別のノードに適用された場合、有効ではありません。

PGDレプリケーションは基本的に他のノードでトランザクションを再生するため、適用されるトランザクションと受信ノードでコミットされたトランザクションとの間に競合がある場合、再生操作は失敗する可能性があります。

Postgresにはそれを防ぐトランザクション間通信メカニズムがあるため、すべてのトランザクションが単一のノードで実行されている場合、ほとんどの競合は発生しません。これらのメカニズムの例は、UNIQUE インデックス、SEQUENCE 操作、行およびリレーションロック、SERIALIZABLE 依存関係追跡などです。これらのメカニズムはすべて、進行中のトランザクション間で通信して、望ましくない同時実行の問題を防ぐ方法です。

PGDには、分散トランザクションマネージャーもロックマネージャーもありません。これが、レイテンシーとネットワークパーティションで良好なパフォーマンスを発揮する理由の一部です。その結果、異なるノード上のトランザクションは、デフォルトの遅延レプリケーションを使用する場合、互いに完全に独立して実行されます。ノード間の独立性が低いほど、競合を完全に回避できます。これが、これが重要な場合にPGDがEager Replicationも提供する理由です。

競合の回避または許容#

ほとんどの場合、競合を回避または許容するようにアプリケーションをデザインできます。

競合は、マルチプルのノードで同時に発生している場合にのみ発生します。競合を回避する最も簡単な方法は、1つのノードにのみ書き込むか、一度に1つの特定のノードから特定の方法で特定の行のみに書き込むことです。

この回避は、多くのアプリケーションで自然に発生します。たとえば、多くのコンシューマアプリケーションでは、所有ユーザーのみがアカウントのデフォルトの請求先住所の変更など、データを変更できます。このようなデータの変更では、更新の競合が発生することはほとんどありません。

ノードがダウンする直前に変更を加える可能性があるため、変更は失われるようです。次に、同じ変更を再度行うと、別のノードで2つの更新が発生する場合があります。ダウンしたノードが復旧すると、古い変更を他のノードに送信しようとします。データの最終更新が保持されるため、リジェクトされます。

INSERT / INSERT 競合の場合、 global sequences を使用してこのタイプの競合を防止します。

部屋予約アプリケーションなどのオブジェクト間の関係を割り当てるアプリケーションの場合、update_if_newer を適用すると、許容できるビジネス結果が得られない場合があります。つまり、同じ部屋を予約したことを2人に個別に確認することは役に立ちません。最も簡単な解決策は、 Eager Replicationを使用して、1つの予約のみが成功するようにすることです。アプリケーションによっては、より複雑な方法が可能である場合があります。たとえば、各ノードに100席を割り当て、そのノードのライタがそれらを予約できるようにします。ただし、ローカルで利用できるものがない場合は、ほとんどのシートが予約された後に分散ロックスキームまたはEager Replicationを使用します。

特定の種類の更新が特定のノードからのみ発生するようにする別の手法は、異なるノードを介して異なる種類のトランザクションをルーティングすることです。例

  • 1つのノードで小包を受信するが、別のノードを使用して小包を配送する

  • 1つのノードで注文が入力され、2番目のノードで作業が準備され、別のノードで顧客に提供されるサービスアプリケーション

多くの場合、最良のコースは、競合の発生を許可し、競合に対処するためにPGDの競合解決メカニズムと連携するようにアプリケーションをデザインすることです。