從 SQL Server 2019 (15.x) CU13 開始,您可以設定對等複寫,以允許最近的插入或更新贏得衝突,以自動解決衝突。 如果任一寫入刪除資料列,SQL Server 會允許刪除贏得衝突。 此方法稱為上次寫入獲勝。
使用預存程序來設定上次寫入獲勝。 當您使用最後一個寫入器時,請勿使用點對點拓撲精靈來新增或移除節點。
重要組態考量
備註
更新至 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) CU13 中引進的。 預設值originatorid會根據原始識別碼解決衝突,而且與 SQL Server 2019 (15.x) CU13 之前的衝突解決相同。設定
@p2p_continue_onconflict = 'true'為允許散發代理程式解決衝突。
當您新增文章 ()
sp_addarticle時,請確認更新命令 ()@upd_cmd的命令類型行為。 這些選項包括:-
CALL(預設) SCALL
詳情請參閱 sp_addarticle。
-
當您在具有最後一個寫入器衝突偵測原則的發行集中新增文章 (
sp_addarticle) 時,預設會使用CALL或SCALL作為參數CALL的@upd_cmd命令類型。備註
SQL Server 支援
SCALL@upd_cmd. 使用SCALL時,當交易將值更新為相同的值時,它不會被視為變更,且SCALL格式不會提供未更新或修改之資料行的值。 如需 SCALL 呼叫格式的詳細資訊,請參閱 預存程序的呼叫語法。您可以使用對等發佈搭配可用性群組中的最後一個寫入器衝突偵測和解決。 See:
您可以看到衝突及其解決方案。
- 在 SQL Server Management Studio 中,以滑鼠右鍵按一下發行集,然後選取 [檢視衝突]。
或
-
conflict_schemaname_tablename查詢發佈資料庫。 例如:conflict_dbo_tab1。 請參閱 conflict_<schema>_<table> (Transact-SQL)。
插入和更新衝突會根據上一個寫入器來解決,但刪除一律會優先。 例如,如果您有刪除/更新衝突,且更新是稍後完成的,則刪除仍會勝出。
最後一個寫入器衝突偵測和解決是根據隱藏資料行
$sys_mw_cd_id來決定。 此資料行的資料類型是 datetime2。
衝突偵測比較
下表比較傳統點對點複寫偵測和解決衝突的方式,以及啟用最後一個寫入器衝突解決的時間:
| 衝突類型 | 衝突詳細資料 | 點對點 | 最後一位作者 |
|---|---|---|---|
| Insert-Insert | 每個資料表中參與點對點複寫的所有資料列都會使用主索引鍵值來唯一識別。 當在多個節點插入具有相同索引鍵值的列時,就會發生插入-插入衝突。 | 如果傳入資料列是獲勝者,則我們會更新目的地資料列。 無論哪種情況,我們都會記錄資訊。 | 如果傳入資料列是獲勝者,則我們會更新目的地資料列。 無論哪種情況,我們都會記錄資訊。 |
| Update-Update | 當相同列在多個節點上更新時發生。 | 如果傳入的行是獲勝者,那麼我們只修改更改的列。 | 如果傳入列是獲勝者,則我們修改目的地的所有列(如果 @upd_cmd 設定為 default – CALL)。 |
| Update-Insert | 如果在一個節點上更新了資料列,但刪除了相同的資料列,然後在另一個節點上重新插入,則會發生。 | 如果傳入的行是獲勝者,那麼我們只修改更改的列。 | 當列更新 , peer1 且相同的列被刪除並重新插入 peer2時,就會發生這種情況。 同步處理發生時,會刪除 上的 peer1 資料列,因為刪除一律會獲勝,然後插入相同的資料列,而資料列會在稍後更新時更新 peer2 。 這會導致不收斂。 |
| 插入 - 更新 | 如果刪除資料列,然後在一個節點上重新插入,且在另一個節點上更新了相同的資料列,則會發生。 | 如果傳入的列是獲勝者,則我們會更新所有資料行。 | 當刪除並重新插入列,並在 上peer1peer2更新相同的列時,就會發生這種情況。 同步處理發生時,會刪除 peer2 資料列,因為刪除一律會獲勝,然後再次插入。 在 上, peer1會略過更新。 |
| Delete-Insert Insert-Delete |
如果在一個節點上刪除了資料列,但刪除了相同的資料列,然後在另一個節點上重新插入,則發生。 | 我們目前認為這是 D-U 衝突,如果傳入的行是獲勝者,那麼我們會從目的地刪除該行。 | 當刪除 peer1 資料列時,會發生這種情況,而相同的資料列被刪除 + 重新插入 peer2。 同步處理發生時,會刪除 上的 peer2 列,而插入 上的列 peer1。 發生這種情況是因為我們不儲存已刪除資料列的相關資訊,因此我們不知道該資料列是否已刪除或不存在於對等節點上。 這會導致不收斂。 |
| Delete-Update | 如果在一個節點上刪除了資料列,但在另一個節點上更新了相同的資料列,則會發生。 | 我們目前認為這是 D-U 衝突,如果傳入資料列是獲勝者,則我們會從目的地刪除該資料列。 | 這是一場民主黨的衝突。 由於刪除總是獲勝,因此傳入刪除是獲勝者,我們從目的地刪除該列。 |
| Update-Delete | 如果在一個節點上更新了資料列,但在另一個節點上刪除了相同的資料列,則會發生。 | 在點對點 Update 預存程式中,如果發生 U-D 衝突,我們會列印下列訊息,而且不會解決它。An update-delete conflict was detected and unresolved. The row could not be updated since the row does not exist. |
這是一場 U-D 衝突。 由於刪除一律會獲勝,因此會略過傳入更新。 |
| Delete-Delete | 在多個節點上刪除資料列時發生。 | 在點對點刪除預存程序中,如果存在 D-D 衝突,則我們不會處理任何更改,只需記錄它。 | 如果存在 D-D 衝突,那麼我們不會處理任何更改,只是記錄下來。 |
備註
在最後一個寫入器衝突偵測原則的目前實作中,當發生插入-刪除、刪除-插入或更新-刪除衝突時,刪除一律會獲勝。
範例
在第一個對等(節點1)上建立發佈
在此範例中,指令碼:
- 發佈名為
MWPubDB的資料庫。 - 將出版物
PublMW命名為 。 - 設定衝突偵測和解決原則,因為上次寫入獲勝:
, @p2p_continue_onconflict= 'true', @p2p_conflictdetection_policy = 'lastwriter'
USE [MWPubDB];
EXECUTE sp_replicationdboption
@dbname = N'MWPubDB',
@optname = N'publish',
@value = N'true';
GO
-- Adding the transactional publication
USE [MWPubDB];
EXECUTE 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];
EXECUTE 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) 上建立發行集。
USE [MWPubDB];
EXECUTE sp_replicationdboption
@dbname = N'MWPubDB',
@optname = N'publish',
@value = N'true';
GO
-- Adding the transactional publication
USE [MWPubDB];
EXECUTE 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];
EXECUTE 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];
EXECUTE 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
EXECUTE 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
建立從 Node2 到 Node1 的訂閱
USE [MWPubDB];
EXECUTE 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
EXECUTE 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