Compartilhar via


Configurar a resolução e detecção de conflitos do último gravador

A partir do SQL Server 2019 (15.x) CU 13, você pode configurar a replicação ponto a ponto para resolver conflitos automaticamente, permitindo que a inserção ou atualização mais recente ganhe o conflito. Se uma das gravações excluir a linha, o SQL Server permitirá a exclusão para superar o conflito. Esse método é chamado de Last Writer Wins.

Use procedimentos armazenados para configurar a última gravação vence. Não use o Assistente de Topologia Ponto a Ponto para adicionar ou remover nós quando você usar o último gravador.

Importantes considerações de configuração

Observação

Depois de atualizar para SQL Server 2019 (15.x) CU13 ou superior, quando você configura uma publicação com a resolução de conflitos última gravação vence, metadados adicionais são incluídos na publicação. Se posteriormente você desinstalar o SQL Server 2019 (15.x) CU13 ou fizer downgrade para uma versão inferior, esses metadados adicionais causarão problemas. Recomendamos descartar essas publicações antes de fazer o downgrade e recriá-las na versão inferior.

Ao configurar a replicação ponto a ponto com a descoberta e a resolução de conflitos automáticas com o método última gravação vence, inclua as seguintes configurações:

  • Crie a publicação com os seguintes parâmetros:

    • Defina @p2p_conflictdetection_policy = 'lastwriter' para especificar a última gravação vence. Confira sp_addpublication (Transact-SQL). Esse parâmetro é introduzido no SQL Server 2019 (15.x) 13. O valor padrão originatorid resolve o conflito com base na ID do originador e é o mesmo que a resolução de conflitos antes do SQL Server 2019 (15.x) CU 13.
    • Defina @p2p_continue_onconflict = 'true' para permitir que o agente de distribuição resolva o conflito.
  • Ao adicionar o artigo (sp_addarticle), confirme o comportamento de tipo do comando de atualização (@upd_cmd). As opções incluem:

    • CALL (Padrão)
    • SCALL

    Confira os detalhes em sp_addarticle (Transact-SQL).

  • Ao adicionar um artigo (sp_addarticle) a uma publicação com a política de detecção de conflito do último gravador, use CALL ou SCALL como tipo de comando para o parâmetro @upd_cmd. O padrão é CALL.

    Observação

    O SQL Server dá suporte para SCALL ou @upd_cmd. Com o SCALL, quando uma transação atualiza um valor para o mesmo valor, ela não é considerada como uma alteração, e o formato SCALL não fornece o valor para colunas que não são atualizadas ou modificadas. Confira mais detalhes sobre o formato de chamada SCALL em Sintaxe de chamada para procedimentos armazenados.

  • Você pode usar a publicação ponto a ponto com a detecção e resolução de conflitos do último gravador em um grupo de disponibilidade. Consulte:

  • Você pode ver o conflito e a resolução.

    • No SQL Server Management Studio, clique com o botão direito do mouse na publicação e selecione Exibir Conflitos.

    Ou

  • Os conflitos de inserção e de atualização são resolvidos com base no último gravador, mas a exclusão sempre prevalece. Por exemplo, caso haja um conflito entre uma exclusão e uma atualização posterior, a exclusão ainda vence.

  • A detecção e a resolução de conflitos do último gravador são determinadas com base na coluna oculta $sys_mw_cd_id. O tipo de dados da coluna é datetime2.

Comparação de detecção de conflitos

A seguinte tabela compara como os conflitos são detectados e resolvidos com a replicação ponto a ponto tradicional e quando a resolução de conflitos do último gravador é habilitada:

Tipo de conflito Detalhes do conflito Ponto a ponto Último gravador
Inserção – inserção Todas as linhas em cada tabela que participa da replicação ponto a ponto são identificadas exclusivamente usando valores de chave primários. Um conflito inserção-inserção ocorre quando uma linha com o mesmo valor de chave é inserida em mais de um nó. Se a linha de entrada for a vencedora, atualizaremos a linha de destino. Em ambos os casos, registramos as informações. Se a linha de entrada for a vencedora, atualizaremos a linha de destino. Em ambos os casos, registramos as informações.
Atualização – atualização Ocorre quando a mesma linha foi atualizada em mais de um nó. Se a linha de entrada for a vencedora, modificaremos APENAS as colunas alteradas. Se a linha de entrada for a vencedora, modificaremos todas as colunas no destino (se @upd_cmd estiver definido como default – CALL).
Atualização – inserção Ocorre quando uma linha é atualizada em um nó, mas a mesma linha é excluída e reinserida em outro nó. Se a linha de entrada for a vencedora, modificaremos APENAS as colunas alteradas. Isso ocorre quando uma linha é atualizada no peer1, e a mesma linha é excluída e inserida novamente no peer2. Quando a sincronização ocorre, a linha no peer1 é excluída (já que a exclusão sempre vence), e a mesma linha é inserida, enquanto a linha é atualizada em peer2 já que a atualização aconteceu depois. Isso resulta em não convergência.
Inserção – atualização Ocorre quando uma linha é excluída e reinserida em um nó, e a mesma linha é atualizada em outro nó. Se a linha de entrada for a vencedora, atualizaremos todas as colunas. Isso ocorre quando uma linha é excluída e reinserida no peer1, e a mesma linha é atualizada no peer2. Quando a sincronização ocorre, a linha é excluída no peer2 (já que a exclusão sempre vence) e depois inserida novamente. No peer1, a atualização é ignorada.
Exclusão – Inserção

Inserção – Exclusão
Ocorre se uma linha foi excluída em um nó, mas a mesma linha foi excluída e, em seguida, reinserida em outro nó. No momento, acreditamos que isso é um conflito D-U. Portanto, se a linha de entrada for a vencedora, excluiremos a linha do destino. Isso ocorre quando uma linha é excluída no peer1, e a mesma linha é excluída e reinserida no peer2. Quando a sincronização ocorre, a linha no peer2 é excluída, enquanto a linha é inserida no peer1. Isso ocorre porque não armazenamos informações sobre a linha excluída, portanto, não sabemos se ela foi excluída ou não estava presente no par. Isso resulta em não convergência.
Exclusão – Atualização Ocorre quando uma linha é excluída em um nó, mas a mesma linha é atualizada em outro nó. No momento, acreditamos que isso é um conflito D-U. Portanto, se a linha de entrada for a vencedora, excluiremos a linha do destino. Este é um conflito D-U. Como a exclusão sempre vence, a exclusão de entrada será a vencedora, e excluiremos a linha do destino.
Atualização – Exclusão Ocorre se uma linha foi atualizada em um nó, mas a mesma linha foi excluída em outro. No procedimento armazenado de atualização ponto a ponto, quando há um conflito U-D, imprimimos a mensagem a seguir e não o resolvemos.

An update-delete conflict was detected and unresolved. The row could not be updated since the row does not exist.
Este é um conflito U-D. Como a exclusão sempre vence, a atualização de entrada é ignorada.
Exclusão – Exclusão Ocorre quando uma linha foi excluída em mais de um nó. No procedimento armazenado de exclusão ponto a ponto, quando há um conflito D-D, não processamos alterações e apenas o registramos. Quando há um conflito D-D, não processamos alterações e apenas o registramos.

Observação

Na implementação atual da política de detecção de conflitos do último gravador, a exclusão sempre vence quando há um conflito inserção – exclusão ou atualização – exclusão.

Exemplos

Criar publicação no primeiro par (Node1)

Neste exemplo, o script faz o seguinte:

  • Publica um banco de dados chamado MWPubDB.
  • Dá o nome PublMW à publicação.
  • Configura a política de detecção e resolução de conflitos como última gravação vence:
    , @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

Criar publicação no segundo par (Node2)

O script abaixo cria a publicação no segundo par (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

Criar assinatura de Node1 para 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

Criar assinatura de Node2 para 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