最後のライター競合の検出と解決を構成する

SQL Server 2019 (15.x) CU 13 以降では、最新の挿入または更新で競合が優先されるようにすることで、競合を自動的に解決するようにピア ツー ピア レプリケーションを構成できます。 いずれかの書き込みで行が削除された場合、SQL Server では、その削除によって競合を優先できます。 この方法は、最後の書き込み優先と呼ばれます。

最後の書き込み優先を構成するには、ストアド プロシージャを使用します。 最後の書き込みを使用するときに、ピア ツー ピア トポロジ ウィザードを使用してノードを追加または削除しないでください。

構成に関する重要な考慮事項

Note

SQL Server 2019 (15.x) CU13 以上に更新した後、競合解決を最後の書き込み優先に設定してパブリケーションを構成すると、追加のメタデータがパブリケーションに含まれます。 後でアンインストールするか、SQL Server 2019 (15.x) CU13 以前のリリースにダウングレードすると、この追加のメタデータが原因で問題が発生します。 ダウングレードする前に、このようなパブリケーションを削除してから、下位バージョンで再作成することをお勧めします。

最後の書き込み優先として解決するために、自動競合検出と解決を使用してピア ツー ピア レプリケーションを構成する場合、次の構成と設定を含めます。

  • 次のパラメーターを使用してパブリケーションを作成します。

    • @p2p_conflictdetection_policy = 'lastwriter' を設定して、最後の書き込み優先を指定します。 「sp_addpublication (Transact-SQL)」を参照してください。 このパラメーターは、SQL Server 2019 (15.x) CU 13 で導入されています。 既定値 originatorid は、発信元 ID に基づいて競合を解決します。この値は、SQL Server 2019 (15.x) CU 13 より前の競合解決と同じです。
    • @p2p_continue_onconflict = 'true' を設定して、ディストリビューション エージェントで競合を解決できるようにします。
  • アーティクル (sp_addarticle) を追加する場合、更新コマンド (@upd_cmd) のコマンドの種類の動作を確認します。 次のオプションがあります。

    • CALL (既定値)
    • SCALL

    sp_addarticle (Transact-SQL)」を参照してください。

  • 最後の書き込みの競合検出ポリシーを使用してパブリケーションにアーティクル (sp_addarticle) を追加する場合、@upd_cmd パラメーターのコマンドの種類として CALL、または SCALL を使用します。CALL が既定値です。

    Note

    SQL Server では、@upd_cmd に対して SCALL がサポートされます。 SCALL では、トランザクションによってある値が同じ値に更新される場合、変更とは見なされず、更新または変更されていない列の値は SCALL 形式で提供されません。 SCALL 呼び出し形式の詳細については、「ストアド プロシージャの呼び出し構文」をご確認ください。

  • ピア ツー ピア パブリケーションは、可用性グループ内で最後の書き込みの競合検出と解決と共に使用できます。 参照トピック

  • 競合とその解決方法を確認できます。

    • SQL Server Management Studio で、パブリケーションを右クリックし、[競合の表示] を選択します。

    または

    • パブリケーション データベースに対して conflict_schemaname_tablename のクエリを実行します (例: conflict_dbo_tab1)。 「conflict_<schema>_<table> (Transact-SQL)」を参照してください。
  • 挿入および更新の競合は、最後の書き込みに基づいて解決されますが、削除は常に優先されます。 たとえば、削除と更新の競合があり、更新が最後に行われた場合でも、削除が優先されます。

  • 最後の書き込みの競合検出と解決は、非表示の列 $sys_mw_cd_id に基づいて決定されます。 この列のデータ型は、datetime2 です。

競合検出の比較

次の表では、競合の検出と解決の方法について、従来のピア ツー ピア レプリケーションと、最後の書き込みの競合解決が有効になっている場合とを比較しています。

競合の種類 競合の詳細 ピア ツー ピア 最後の書き込み
挿入と挿入 ピア ツー ピア レプリケーションに参加している各テーブルのすべての行は、主キー値を使用して一意に識別されます。 挿入と挿入の競合は、キー値の同じ行が複数のノードで挿入されたときに発生します。 受信した行が優先される場合、変換先の行が更新されます。 いずれの場合も、情報が記録されます。 受信した行が優先される場合、変換先の行が更新されます。 いずれの場合も、情報が記録されます。
更新と更新 複数のノードで同じ行が更新されたときに発生します。 受信した行が優先される場合、変更された列のみが変更されます。 受信した行が優先される場合、変換先のすべての列が変更されます (@upd_cmddefault - CALL に設定されている場合)。
更新と挿入 あるノードで行が更新され、別のノードで同じ行が削除された後に再挿入された場合に発生します。 受信した行が優先される場合、変更された列のみが変更されます。 これは、ある行が peer1 で更新され、同じ行が peer2 で削除されて再挿入された場合に発生します。 同期が発生すると、削除が常に優先されるため、peer1 では行が削除されてから同じ行が挿入されますが、peer2 では、後で更新されたときに行が更新されます。 これにより、非収束が発生します。
挿入と更新 あるノードで行が更新された後に再挿入され、別のノードで同じ行が更新された場合に発生します。 受信した行が優先される場合、すべての列が更新されます。 これは、ある行が peer1 で削除されて再挿入され、同じ行が peer2 で更新された場合に発生します。 同期が発生すると、削除が常に優先されるため、peer2 では行が削除されてから再度挿入されます。 peer1 では、更新がスキップされます。
削除と挿入

挿入と削除
あるノードで行が削除され、別のノードで同じ行が削除された後に再挿入された場合に発生します。 現在、これは D-U の競合であると考えられており、受信した行が優先される場合は、変換先から行が削除されます。 これは、ある行が peer1 で削除され、同じ行が peer2 で削除されて再挿入された場合に発生します。 同期が発生すると、peer2 では行が削除され、peer1 では行が挿入されます。 これは、削除された行に関する情報が保存されていないため、その行が削除されたのか、ピアに存在しなかったかがわからないために発生します。 これにより、非収束が発生します。
削除と更新 あるノードで行が削除され、別のノードで同じ行が更新された場合に発生します。 現在、これは D-U の競合であると考えられており、受信した行が優先される場合は、変換先から行が削除されます。 これは、D-U 競合です。 常に削除が優先されるため、受信した削除が優先され、変換先から行が削除されます。
更新と削除 あるノードで行が更新され、別のノードで同じ行が削除された場合に発生します。 ピアツーピア更新ストアド プロシージャでは、U-D 競合がある場合、次のメッセージが出力され、競合は解決されません。

An update-delete conflict was detected and unresolved. The row could not be updated since the row does not exist.
これは、U-D 競合です。 削除が常に優先されるため、受信した更新はスキップされます。
削除と削除 複数のノードで同じ行が削除された場合に発生します。 ピア ツー ピア削除ストアド プロシージャでは、D-D 競合がある場合、変更は処理されず、記録されるだけです。 D-D 競合がある場合、変更は処理されず、記録されるだけです。

Note

最後の書き込みの競合検出ポリシーの現在の実装では、挿入と削除、削除と挿入、または更新と削除の競合がある場合、削除が常に優先されます。

最初のピア (ノード 1) でパブリケーションを作成する

この例のスクリプトは、次のことを実行します。

  • MWPubDB という名前のデータベースを公開します。
  • パブリケーションに PublMW という名前を付けます。
  • 競合検出と解決ポリシーを最後の書き込み優先として構成します。
    , @p2p_continue_onconflict= 'true'
    , @p2p_conflictdetection_policy = 'lastwriter'
USE [MWPubDB]
EXEC sp_replicationdboption @dbname = N'MWPubDB'
  , @optname = N'publish'
  , @value = N'true'
GO
-- Adding the transactional publication
USE [MWPubDB]
EXEC sp_addpublication @publication = N'PublMW'
  , @description = N'Peer-to-Peer publication of database ''MWPubDB'' from Publisher ''Node1''.'
  , @sync_method = N'native'
  , @retention = 0
  , @allow_push = N'true'
  , @allow_pull = N'true'
  , @allow_anonymous = N'false'
  , @enabled_for_internet = N'false'
  , @snapshot_in_defaultfolder = N'true'
  , @compress_snapshot = N'false'
  , @ftp_port = 21
  , @allow_subscription_copy = N'false'
  , @add_to_active_directory = N'false'
  , @repl_freq = N'continuous'
  , @status = N'active'
  , @independent_agent = N'true'
  , @immediate_sync = N'true'
  , @allow_sync_tran = N'false'
  , @allow_queued_tran = N'false'
  , @allow_dts = N'false'
  , @replicate_ddl = 1, @allow_initialize_from_backup = N'true'
  , @enabled_for_p2p = N'true'
  , @enabled_for_het_sub = N'false'
  , @p2p_conflictdetection = N'true'
  , @p2p_originator_id = 100
  , @p2p_continue_onconflict= 'true'
  , @p2p_conflictdetection_policy = 'lastwriter'
GO

USE [MWPubDB]
EXEC sp_addarticle @publication = N'PublMW'
  , @article = N'tab1'
  , @source_owner = N'dbo'
  , @source_object = N'tab1'
  , @type = N'logbased'
  , @description = null
  , @creation_script = null
  , @pre_creation_cmd = N'drop'
  , @schema_option = 0x0000000008035DDB
  , @identityrangemanagementoption = N'manual'
  , @destination_table = N'tab1'
  , @destination_owner = N'dbo'
  , @status = 16
  , @vertical_partition = N'false'
  , @ins_cmd = N'CALL sp_MSins_dbotab1'
  , @del_cmd = N'CALL sp_MSdel_dbotab1'
  , @upd_cmd = N'CALL sp_MSupd_dbotab1'
GO

2 番目のピア (ノード 2) でパブリケーションを作成する

次のスクリプトでは、2 番目のピア (ノード 2) でパブリケーションを作成します。

USE [MWPubDB]
EXEC sp_replicationdboption @dbname = N'MWPubDB'
 , @optname = N'publish'
 , @value = N'true'
GO
-- Adding the transactional publication
USE [MWPubDB]
EXEC sp_addpublication @publication = N'PublMW'
 , @description = N'Peer-to-Peer publication of database ''MWPubDB'' from Publisher ''Node2''.'
 ,@sync_method = N'native'
 , @retention = 0, @allow_push = N'true'
 , @allow_pull = N'true'
 , @allow_anonymous = N'false'
 , @enabled_for_internet = N'false'
 , @snapshot_in_defaultfolder = N'true'
 , @compress_snapshot = N'false'
 , @ftp_port = 21, @allow_subscription_copy = N'false'
 , @add_to_active_directory = N'false'
 , @repl_freq = N'continuous'
 , @status = N'active'
 , @independent_agent = N'true'
 , @immediate_sync = N'true'
 , @allow_sync_tran = N'false'
 , @allow_queued_tran = N'false'
 , @allow_dts = N'false'
 , @replicate_ddl = 1, @allow_initialize_from_backup = N'true'
 , @enabled_for_p2p = N'true'
 , @enabled_for_het_sub = N'false'
 , @p2p_conflictdetection = N'true'
 , @p2p_originator_id = 1
 , @p2p_continue_onconflict= 'true'
,  @p2p_conflictdetection_policy = 'lastwriter'
GO

USE [MWPubDB]
EXEC sp_addarticle @publication = N'PublMW'
 , @article = N'tab1'
 , @source_owner = N'dbo'
 , @source_object = N'tab1'
 , @type = N'logbased'
 , @description = null
 , @creation_script = null
 , @pre_creation_cmd = N'drop'
 , @schema_option = 0x0000000008035DDB
 , @identityrangemanagementoption = N'manual'
 , @destination_table = N'tab1'
 , @destination_owner = N'dbo'
 , @status = 16, @vertical_partition = N'false'
 , @ins_cmd = N'CALL sp_MSins_dbotab1'
 , @del_cmd = N'CALL sp_MSdel_dbotab1'
 , @upd_cmd = N'CALL sp_MSupd_dbotab1'
GO

ノード 1 からノード 2 へのサブスクリプションを作成する

USE [MWPubDB]
EXEC sp_addsubscription @publication = N'PublMW'
 , @subscriber = N'Node2'
 , @destination_db = N'MWPubDB'
 , @subscription_type = N'Push'
 , @sync_type = N'replication support only'
 , @article = N'all'
 , @update_mode = N'read only'
 , @subscriber_type = 0
GO
EXEC sp_addpushsubscription_agent @publication = N'PublMW'
 , @subscriber = N'Node2'
 , @subscriber_db = N'MWPubDB'
 , @job_login = null
 , @job_password = null
 , @subscriber_security_mode = 1
 , @frequency_type = 64
 , @frequency_interval = 1
 , @frequency_relative_interval = 1
 , @frequency_recurrence_factor = 0
 , @frequency_subday = 4
 , @frequency_subday_interval = 5
 , @active_start_time_of_day = 0
 , @active_end_time_of_day = 235959
 , @active_start_date = 0 
 , @active_end_date = 0
 , @dts_package_location = N'Distributor'
GO

ノード 2 からノード 1 へのサブスクリプションを作成する

USE [MWPubDB]
EXEC sp_addsubscription @publication = N'PublMW'
 , @subscriber = N'Node1'
 , @destination_db = N'MWPubDB'
 , @subscription_type = N'Push',
@sync_type = N'replication support only'
 , @article = N'all'
 , @update_mode = N'read only'
 , @subscriber_type = 0
go
EXEC sp_addpushsubscription_agent @publication = N'PublMW'
 , @subscriber = N'Node1'
 , @subscriber_db = N'MWPubDB'
 , @job_login = null
 , @job_password = null
 , @subscriber_security_mode = 1
 , @frequency_type = 64
 , @frequency_interval = 1
 , @frequency_relative_interval = 1
 , @frequency_recurrence_factor = 0
 , @frequency_subday = 4
 , @frequency_subday_interval = 5
 , @active_start_time_of_day = 0
 , @active_end_time_of_day = 235959
 , @active_start_date = 0
 , @active_end_date = 0
 , @dts_package_location = N'Distributor'
GO

関連項目