Настройка обнаружения и разрешения конфликтов последнего модуля записи

Начиная с SQL Server 2019 (15.x) CU 13, вы можете настроить одноранговое реплика tion для автоматического разрешения конфликтов, позволяя последней вставке или обновлению, чтобы выиграть конфликт. Если любая из двух операций записи удаляет строку, 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) CU 13. Значение originatorid по умолчанию разрешает конфликт на основе идентификатора источника и совпадает с разрешением конфликтов до SQL Server 2019 (15.x) CU 13.
    • Задайте параметр @p2p_continue_onconflict = 'true', чтобы разрешить агенту распространения решать конфликт.
  • При добавлении статьи (sp_addarticle) подтвердите тип действия для команды обновления (@upd_cmd). Возможные варианты:

    • CALL (по умолчанию)
    • SCALL

    Дополнительные сведения: sp_addarticle (Transact-SQL).

  • Когда вы добавляете статью (sp_addarticle) в публикацию с политикой обнаружения конфликтов последнего модуля записи, используйте CALL или SCALL в качестве параметра типа команды для @upd_cmd. По умолчанию используется CALL.

    Примечание.

    SQL Server поддерживает SCALL для @upd_cmd. Если SCALLтранзакция обновляет значение до того же значения, он не считается изменением и SCALL форматом не предоставляет значение столбцов, которые не обновляются или не изменяются. Сведения о формате вызова SCALL см. в разделе Синтаксис вызова для хранимых процедур.

  • Вы можете использовать одноранговую публикацию с обнаружением и разрешением конфликтов последнего модуля записи в группе доступности. См.

  • Вы можете просмотреть конфликт и его разрешение.

    • В SQL Server Management Studio щелкните публикацию правой кнопкой мыши и выберите Просмотр конфликтов.

    Or

    • Выполните запрос conflict_schemaname_tablename в базе данных публикации. Например, conflict_dbo_tab1. См. статью conflict_<schema>_<table> (Transact-SQL).
  • Конфликты вставки и обновления разрешаются на основе последнего модуля записи, но операция удаления всегда получает приоритет. Например, если имеется конфликт "удаление-обновление" и обновление было выполнено позже, операция удаления все равно побеждает.

  • Обнаружение и разрешение конфликтов последнего модуля записи выполняется на основе скрытого столбца $sys_mw_cd_id. Тип данных этого столбца — datetime2.

Сравнение обнаружения конфликтов

В следующей таблице сравниваются способы обнаружения и разрешения конфликтов при традиционной одноранговой репликации и при включенном разрешении конфликтов на основе последнего модуля записи.

Тип конфликта Сведения о конфликте Одноранговой Последний модуль записи
Вставка-вставка Все строки во всех таблицах, участвующих в одноранговой репликации, уникальным образом идентифицируются с помощью значений первичного ключа. Конфликт «вставка-вставка» имеет место тогда, когда вставка строки с одним и тем же значением ключа осуществляется более чем на одном узле. Если входящая строка имеет приоритет, мы обновляем целевую строку. В любом случае мы записываем информацию. Если входящая строка имеет приоритет, мы обновляем целевую строку. В любом случае мы записываем информацию.
Обновление-обновление Происходит, когда одна и та же строка обновляется более чем на одном узле. Если входящая строка имеет приоритет, мы изменяем ТОЛЬКО измененные столбцы. Если входящая строка имеет приоритет, мы изменяем все столбцы в месте назначения (если для @upd_cmd задано default — CALL).
Обновление-вставка Происходит, когда строка обновляется на одном узле, а на другом узле та же строка удаляется и вставляется повторно. Если входящая строка имеет приоритет, мы изменяем ТОЛЬКО измененные столбцы. Происходит, когда строка обновляется на одноранговом узле peer1 и та же строка удаляется и повторно вставляется на peer2. При синхронизации строка на peer1 удаляется, так как удаление всегда имеет приоритет, а затем выполняется ее же вставка. Однако строка на peer2 обновляется, так как обновление произошло позже. Это нарушает конвергенцию данных.
Вставка-обновление Происходит, когда строка удаляется и затем повторно вставляется на одном узле, а на другом узле та же строка обновляется. Если входящая строка имеет приоритет, мы обновляем все столбцы. Происходит, когда строка удаляется и повторно вставляется на одноранговом узле peer1 и та же строка обновляется на peer2. При синхронизации строка на узле peer2 удаляется, так как удаление всегда имеет приоритет, а затем строка вставляется повторно. На узле peer1 обновление пропускается.
Delete-Insert

Вставка-удаление
Происходит, если строка удаляется на одном узле, а на другом та же строка удаляется и затем вновь вставляется. Сейчас мы рассматриваем это как конфликт "удаление-обновление", и если входящая строка получает приоритет, мы удаляем строку в назначении. Происходит, когда строка удаляется на одноранговом узле peer1 и та же строка удаляется и повторно вставляется на peer2. При синхронизации строка на узле peer2 удаляется, а на peer1 — вставляется. Это происходит потому, что мы не храним сведения об удаленной строке, поэтому мы не знаем, была ли строка удалена или отсутствует в одноранговом узле. Это нарушает конвергенцию данных.
Удаление-обновление Происходит, когда строка удаляется на одном узле, а на другом узле та же строка обновляется. Сейчас мы рассматриваем это как конфликт "удаление-обновление", и если входящая строка получает приоритет, мы удаляем строку в назначении. Это конфликт "удаление-обновление". Так как удаление всегда имеет приоритет, входящая операция удаления будет победителем и мы удаляем строку в назначении.
Обновление-удаление Происходит, когда строка обновляется на одном узле, а на другом та же строка удаляется. При возникновении конфликта "обновление-удаление" в хранимой процедуре обновления одноранговой публикации мы выводим следующее сообщение и не устраняем конфликт.

An update-delete conflict was detected and unresolved. The row could not be updated since the row does not exist.
Это конфликт "обновление-удаление". Так как удаление всегда получает приоритет, входящая операция обновления пропускается.
Удаление-удаление Происходит, когда строка удаляется более чем на одном узле. При возникновении конфликта "удаление-удаление" в хранимой процедуре удаления одноранговой публикации мы не обрабатываем никаких изменений, только создаем о них запись. В случае возникновения конфликта "удаление-удаление" мы не обрабатываем никаких изменений, только создаем о них запись.

Примечание.

В текущей реализации политики обнаружения конфликтов последнего модуля записи, когда имеется конфликт "вставка-удаление", "удаление-вставка" или "обновление-удаление", удаление всегда получает приоритет.

Примеры

Создание публикации на первом одноранговом узле (Node1)

В этом примере скрипт выполняет следующие действия.

  • Публикует базу данных 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

Создание публикации на втором одноранговом узле (Node2)

Приведенный ниже скрипт создает публикацию на втором одноранговом узле (Node2).

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

Создание подписки с узла Node1 на узел Node2

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

Создание подписки с узла Node2 на узел Node1

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

См. также