マージ レプリケーションで競合を検出および解決する方法

マージ レプリケーションでは、複数のノードが独立してデータを変更できるため、1 つのノードでの変更が別のノードでの同じデータに対する変更と競合する場合があります。また、マージ エージェントで制約違反などのエラーが発生し、特定のノードでの変更を別のノードに反映できない場合もあります。このトピックでは、競合の種類、競合の検出および解決方法、および検出や解決に影響を及ぼす要因について説明します。

競合の検出および解決

マージ エージェントは、MSmerge_contents システム テーブルの lineage 列を使用して競合を検出します。アーティクルに対し、列レベルの追跡が有効である場合には、COLV1 列も使用されます。これらの列には、行や列の挿入または更新時期についてのメタデータ、およびマージ レプリケーション トポロジのどのノードが行または列に対して変更を行ったのかについてのメタデータが含まれます。システム ストアド プロシージャ sp_showrowreplicainfo (Transact-SQL) を使用すると、このメタデータを表示できます。

マージ エージェントは、同期中に適用する変更を列挙する際、パブリッシャとサブスクライバの各行に対してメタデータを比較します。マージ エージェントはこのメタデータを使用して、トポロジの 1 つ以上のノードで行または列が変更されているかどうかを判断します。この変更は、競合の可能性を示しています。競合が検出されると、マージ エージェントは競合が発生したアーティクルに対して指定された競合回避モジュールを起動し、このモジュールを使用して競合で優先されるデータを決定します。競合で優先された行がパブリッシャおよびサブスクライバで適用され、優先されなかった行のデータは競合テーブルに書き込まれます。

アーティクルに対してインタラクティブ競合解決を選択しない限り、競合はすぐにマージ エージェントによって自動的に解決されます。詳細については、「インタラクティブな競合解決」を参照してください。マージ レプリケーション競合表示モジュールを使用して競合で優先された行を手動で変更する場合、マージ エージェントは次の同期中に優先された行を優先されなかったサーバーに適用します。

解決済みの競合のログ記録

マージ エージェントは、競合回避モジュールのロジックに従って競合を解決した後、競合の種類に応じて競合データのログを記録します。

  • UPDATE および INSERT 競合の場合、マージ エージェントは優先されなかった行をアーティクルの競合テーブルに書き込みます。このテーブルは conflict_<PublicationName>_<ArticleName> の形式で名前が指定されています。競合の種類などの一般的な競合情報は MSmerge_conflicts_info テーブルに書き込まれます。

  • DELETE 競合の場合、マージ エージェントは優先されなかった行を MSmerge_conflicts_info テーブルに書き込みます。更新が削除より優先されると、優先されなかった行にデータはなく (削除であるため)、conflict_<PublicationName>_<ArticleName> には何も書き込まれません。

各アーティクルの競合テーブルは、sp_addmergepublication@conflict_logging パラメータに指定された値により、パブリケーション データベース、サブスクリプション データベース、またはその両方 (既定) で作成されます。各競合テーブルは、基となるアーティクルと同じ構造を持ち、origin_datasource_id 列が追加されています。マージ エージェントは、データがパブリケーションの競合の保有期間より古いと、競合テーブルからそのデータを削除します。競合の保有期間は、sp_addmergepublication@conflict_retention パラメータを使用して指定します (既定は 14 日)。

レプリケーションでは、競合データを表示するために、レプリケーション競合表示モジュールおよびストアド プロシージャ (sp_helpmergearticleconflictssp_helpmergeconflictrows、および sp_helpmergedeleteconflictrows) が用意されています。詳細については、「マージ パブリケーションでデータの競合を表示および解決する方法 (SQL Server Management Studio)」および「マージ パブリケーションの競合情報を表示する方法 (レプリケーション Transact-SQL プログラミング)」を参照してください。

競合の解決に影響を及ぼす要因

マージ エージェントによる検出済みの競合の解決方法に影響を及ぼす要因は 2 つあります。

  • サブスクリプションの種類 : クライアントまたはサーバー (サブスクリプションがプル サブスクリプションであるかプッシュ サブスクリプションであるかは、競合の解決に影響を与えません)。

  • 使用する競合の追跡の種類 : 行レベル、列レベル、または論理レコード レベル

サブスクリプションの種類

サブスクリプションを作成する際には、プッシュ サブスクリプションであるかプル サブスクリプションであるかの指定に加え、クライアント サブスクリプションであるかサーバー サブスクリプションであるかを指定します。サブスクリプションの作成後は、種類を変更することはできません (以前のバージョンの Microsoft SQL Server では、クライアント サブスクリプションおよびサーバー サブスクリプションは、それぞれ、ローカル サブスクリプション、グローバル サブスクリプションと呼ばれていました)。

優先度値 (0.00 ~ 99.99) を割り当てたサブスクリプションをサーバー サブスクリプション、パブリッシャの優先度値を使用するサブスクリプションをクライアント サブスクリプションと呼んでいます。また、サーバー サブスクリプションを使用するサブスクライバは、別のサブスクライバにデータを再パブリッシュできます。次の表に、各サブスクライバの種類の主な相違点と使用法をまとめます。

種類

優先度値

使用方法

サーバー

ユーザーによる割り当て

サブスクライバごとに異なる優先度を割り当てる場合。

クライアント

0.00。ただし、データ変更の基準は、同期後のパブリッシャの優先度値。

すべてのサブスクライバに同じ優先度を割り当て、パブリッシャと最初にマージしたサブスクライバの値を競合で残す場合。

クライアント サブスクリプションで行が変更された場合、サブスクリプションが同期されるまで、優先度は変更に割り当てられません。同期中には、サブスクライバからの変更にパブリッシャの優先度が割り当てられ、その後の同期でもその優先度が保持されます。ある程度までは、パブリッシャに変更の所有権があると言えます。この動作では、最初のサブスクライバがパブリッシャと同期し、その後の行または列に対する別のサブスクライバとの競合で優先されるようにすることを許可します。

サーバー サブスクリプションで行を変更する場合、サブスクリプションの優先度はその変更のメタデータに格納されます。この優先度値は、他のサブスクライバの変更とマージされるときに、変更された行と共に伝達されます。これによって、優先度の高いサブスクリプションによる変更が、その後の優先度の低いサブスクリプションによる変更に上書きされることを防ぐことができます。

サブスクリプションは、そのパブリッシャより高い明示的な優先度値を持つことはできません。マージ レプリケーション トポロジの最上位レベルのパブリッシャは、常に明示的な優先度値 100.00 を持ちます。このパブリケーションへのすべてのサブスクリプションは、この値より低い優先度値を持つ必要があります。再パブリッシュ トポロジには、以下の注意点があります。

  • サブスクライバがデータを再パブリッシュする場合、サブスクリプションは、サブスクライバの上位のパブリッシャより低い優先度値のサーバー サブスクリプションである必要があります。

  • サブスクライバが再パブリッシュ ツリーのリーフ レベルのためデータを再パブリッシュしない場合、サブスクリプションは、クライアント サブスクリプションである必要があります。

サーバー サブスクリプションと優先度の詳細については、「サブスクリプションの種類と割り当てられた優先度に基づくマージ競合解決の例」を参照してください。

競合通知の遅延

競合通知の遅延は、異なる競合優先度を持つサーバー サブスクリプションで発生する場合があります。次のシナリオを考えます。ここでは、パブリッシャと優先度の低いサブスクライバとの間で競合しない変更が交換されますが、より優先度の高いサブスクライバがパブリッシャと同期すると、変更が競合することになります。

  1. パブリッシャと LowPrioritySub という優先度の低いサブスクライバが、数回の同期にわたって競合なしに変更を交換します。

  2. HighPrioritySub という優先度の高いサブスクライバは、しばらくの間パブリッシャと同期していませんでしたが、LowPrioritySub サブスクライバが変更したのと同じ行を変更しました。

  3. HighPrioritySub サブスクライバがパブリッシャと同期すると、LowPrioritySub サブスクライバよりも優先度が高いため、このサブスクライバの変更と LowPrioritySub サブスクライバとの間の競合で HighPrioritySub サブスクライバが優先されます。これで、パブリッシャには HighPrioritySub サブスクライバによる変更が含まれます。

  4. HighPrioritySub サブスクライバとの競合のため、LowPrioritySub サブスクライバはパブリッシャとマージし、多数の変更をダウンロードします。

優先度の低いサブスクライバが競合で優先されない行と同じ行を変更した場合、この状況は問題となる可能性があります。これにより、このサブスクライバが行ったすべての変更が失われる結果となる場合があります。この問題に対して有効な解決策は、ビジネス ロジックによる制限がない限り、すべてのサブスクライバが同じ優先度を持つようにすることです。

追跡レベル

データ変更が競合と見なされるかどうかは、アーティクルに対して設定した競合の追跡の種類 (行レベル、列レベル、または論理レコード レベル) に依存します。論理レコード レベルの追跡の詳細については、「論理レコードの競合の検出および解決」を参照してください。

競合を行レベルで認識する場合、対応する行が変更されると、その変更が行内の同じ列に加えられたかどうかにかかわらず競合と判断されます。たとえば、パブリッシャ行の住所列が変更され、同じテーブルの対応するサブスクライバ行の電話番号列が変更されたとします。行レベルの追跡では、同じ行が変更されたため、競合が検出されます。列レベルの追跡では、同じ行で異なる列が変更されたため、競合は検出されません。

行レベルおよび列レベルの追跡では、競合の解決方法は同じです。データの行全体が、競合で優先されたデータにより上書きされます (論理レコード レベルの追跡では、解決方法はアーティクル プロパティ logical_record_level_conflict_resolution に依存します)。

通常、アプリケーションのセマンティクスによって、使用する追跡オプションが決まります。たとえば、住所と電話番号のように、通常は同時に入力する顧客データを更新している場合、行レベルの追跡を選択する必要があります。この場合、列レベルの追跡が選択されると、ある場所からの顧客の住所への変更と、別の場所からの同じ顧客の電話番号への変更は、競合として検出されません。同期の際にデータがマージされ、エラーは見逃されます。その他の場合では、各所のサイトから個々の列を更新するのが最も適切という場合もあります。たとえば、2 つのサイトが、収入レベルとクレジット カードによる合計購入額のような、異なる種類の顧客統計情報にアクセスできる場合があります。列レベルの追跡を選択すると、両サイトは不要な競合を避け、異なる列に対して統計データを入力できます。

注意

アプリケーションで列レベルの追跡が必要ない場合は、行レベルの追跡 (既定) の使用をお勧めします。これは一般的に、行レベルの追跡の方が高い同期パフォーマンスが得られるためです。行レベルの追跡を使用すると、ベース テーブルには最大 1,024 列格納することができますが、パブリッシュ対象は最大 246 列なので、それに合わせて列をアーティクルからフィルタで選択する必要があります。列レベルの追跡を使用する場合、ベース テーブルには最大 246 列格納することができます。

競合の種類

競合の大部分は更新に関連する (1 つのノードでの更新が別のノードでの更新または削除と競合する) ものですが、競合の種類は他にもあります。ここで説明する各種の競合は、マージ処理のアップロード フェーズまたはダウンロード フェーズで発生する可能性があります。アップロード処理は、特定のマージ セッションで行われる変更の最初の調整で、マージ エージェントがサブスクライバからの変更をパブリッシャにレプリケートするフェーズです。この処理で検出された競合は、アップロード競合と呼ばれます。ダウンロード処理はパブリッシャからサブスクライバへの変更の移動を含み、アップロード処理の後に発生します。この処理フェーズでの競合は、ダウンロード競合と呼ばれます。

競合の種類に関する詳細については、「MSmerge_conflicts_info (Transact-SQL)」を参照してください。特に conflict_type 列と reason_code 列を参照してください。

更新 - 更新の競合

マージ エージェントは、あるノードでの行 (または、列や論理レコード) に対する更新が、別のノードでの同じ行に対する別の更新と競合する場合に、更新 - 更新の競合を検出します。この場合、既定の競合回避モジュールは、優先された行を優先されなかったノードに送信し、アーティクルの競合テーブルに優先されなかった行のログを記録します。

更新 - 削除の競合

マージ エージェントは、あるノードでのデータの更新が別のノードでの削除と競合する場合に、更新 - 削除の競合を検出します。この場合、マージ エージェントは行を更新しますが、マージ エージェントが更新先で行を検索すると、行は削除されているため見つかりません。優先されたノードが行を更新したノードの場合、優先されなかったノードでの削除は破棄され、マージ エージェントは競合で優先されなかったノードに新しく更新された行を送信します。マージ エージェントは、優先されなかった行の情報を、MSmerge_conflicts_info テーブルにログ記録します。

失敗した変更の競合

マージ エージェントは、特定の変更を適用できない場合にこれらの競合を発生させます。これは通常、パブリッシャとサブスクライバ間での制約定義、および制約の NOT FOR REPLICATION (NFR) プロパティの使用状況の違いにより発生します。たとえば、以下のような場合があります。

  • サブスクライバでの外部キーの競合。これはサブスクライバ側の制約が NFR とマークされていないときに発生する可能性があります。

  • パブリッシャとサブスクライバ間での制約が異なり、それらの制約が NFR とマークされていない場合。

  • サブスクライバで従属オブジェクトが利用できない場合。たとえば、ビューが従属するテーブルではなく、ビューをパブリッシュする場合、サブスクライバでそのビューから挿入を試みるとエラーが発生します。

  • 主キーと外部キーの制約に一致しないパブリケーションの結合フィルタ ロジック。SQL Server のリレーショナル エンジンが制約を受け入れようとする一方、マージ エージェントがアーティクル間の結合フィルタ定義を受け入れようとする場合に、競合が発生します。マージ エージェントは、テーブル レベルの制約のために変更先のノードで変更を適用できず、競合が発生します。

  • 一意のインデックスまたは一意の制約の違反、または主キー違反が原因の競合が、アーティクルに対して ID 列が定義され、自動 ID 管理が使用されていない場合に発生する可能性があります。2 つのサブスクライバが新しく挿入された行に対して同じ ID 値を使用するのであれば、これは問題となる場合があります。ID 範囲の管理の詳細については、「ID 列のレプリケート」を参照してください。

  • トリガ ロジックにより、マージ エージェントが挿入先のテーブルに行を挿入できない競合。サブスクライバで定義される更新トリガを検討します。トリガは NFR としてマークされず、そのロジックには ROLLBACK が含まれています。エラーが発生すると、トリガはトランザクションの ROLLBACK を発行します。これにより、マージ エージェントは失敗した変更の競合を検出します。