Compartilhar via


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

A partir do SQL Server 2019 (15.x) CU13, 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á que a exclusão ganhe o conflito. Esse método é conhecido como última gravação ganha.

Use procedimentos armazenados para configurar as vitórias de última gravação. Não use o Assistente de Topologia Ponto a Ponto para adicionar ou remover nós ao usar o último gravador.

Considerações de configuração importantes

Observação

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

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

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

    • Defina @p2p_conflictdetection_policy = 'lastwriter' para especificar as últimas vitórias de gravação. Consulte sp_addpublication (Transact-SQL). Esse parâmetro é introduzido no SQL Server 2019 (15.x) CU13. O valor originatorid padrão resolve conflitos com base na ID do originador e é o mesmo que a resolução de conflitos antes do SQL Server 2019 (15.x) CU13.

    • 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 do tipo de comando para o comando de atualização (@upd_cmd). As opções incluem:

    • CALL (Padrão)
    • SCALL

    Veja os detalhes em sp_addarticle.

  • Quando você adiciona um artigo (sp_addarticle) em uma publicação com uma política de detecção de conflitos do último gravador, usar CALL ou SCALL como tipo de comando para @upd_cmd parâmetro é CALL padrão.

    Observação

    O SQL Server é SCALL compatível com @upd_cmd. Com SCALL, quando uma transação atualiza um valor para o mesmo valor, ela não é considerada como uma alteração e SCALL o formato não fornece o valor para colunas que não são atualizadas ou modificadas. Para obter mais informações sobre o formato de chamada SCALL, consulte Sintaxe de chamada para procedimentos armazenados.

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

  • Você pode ver o conflito e sua 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 atualização são resolvidos com base no último gravador, mas a exclusão sempre prevalece. Por exemplo, se você tiver conflito de exclusão-atualização e a atualização tiver sido feita posteriormente, a exclusão ainda vencerá.

  • A detecção e a resolução de conflitos do último gravador são determinadas com base em uma coluna $sys_mw_cd_idoculta. O tipo de dados dessa coluna é datetime2.

Comparação de detecção de conflitos

A tabela a seguir 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 está habilitada:

Tipo de conflito Detalhes do conflito Ponto a ponto Último gravador
Insert-Insert Todas as linhas em cada tabela que participam da replicação ponto a ponto são identificadas exclusivamente usando valores de chave primária. Um conflito de inserção ocorre quando uma linha com o mesmo valor de chave foi 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.
Update-Update Ocorre quando a mesma linha foi atualizada em mais de um nó. Se a linha de entrada for a vencedora, modificaremos SOMENTE as colunas alteradas. Se a linha de entrada for o vencedor, modificaremos todas as colunas no destino (se @upd_cmd for definido como defaultCALL).
Update-Insert Ocorre se uma linha foi atualizada em um nó, mas a mesma linha foi excluída e reinserida em outro nó. Se a linha de entrada for a vencedora, modificaremos SOMENTE as colunas alteradas. Isso ocorre quando uma linha é atualizada peer1 e a mesma linha é excluída e reinserida.peer2 Quando a sincronização ocorre, a linha ativada peer1 é excluída, pois a exclusão sempre vence e, em seguida, a mesma linha é inserida, enquanto a linha é atualizada peer2 como atualizada ocorreu posteriormente. Isso leva à não conexão.
Inserir - Atualizar Ocorre se uma linha foi excluída e reinserida em um nó e a mesma linha foi 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 peer1 e a mesma linha é atualizada peer2. Quando a sincronização ocorre, a linha é excluída peer2 , pois a exclusão sempre vence e, em seguida, é inserida novamente. Ativado peer1, a atualização é ignorada.
Delete-Insert

Insert-Delete
Ocorre se uma linha foi excluída em um nó, mas a mesma linha foi excluída e reinserida em outro nó. No momento, achamos isso como conflito de D-U e, se a linha de entrada for vencedora, excluiremos a linha do destino. Isso ocorre quando uma linha é excluída peer1 e a mesma linha é excluída + reinserida.peer2 Quando a sincronização ocorre, a linha ativada peer2 é excluída, enquanto a linha é inserida em peer1. Isso ocorre porque não armazenamos informações sobre a linha excluída, portanto, não sabemos se a linha foi excluída ou não estava presente no par. Isso leva à não conexão.
Delete-Update Ocorre se uma linha foi excluída em um nó, mas a mesma linha foi atualizada em outro nó. No momento, achamos isso como conflito de D-U e, se a linha de entrada for o vencedor, excluiremos a linha do destino. Este é um conflito D-U. Como a exclusão sempre vence, a exclusão de entrada é o vencedor e excluimos a linha do destino.
Update-Delete Ocorre se uma linha foi atualizada em um nó, mas a mesma linha foi excluída em outro nó. No procedimento armazenado atualização ponto a ponto, se houver um conflito U-D, imprimiremos a mensagem a seguir e não a resolveremos.

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.
Delete-Delete Ocorre quando uma linha foi excluída em mais de um nó. No procedimento armazenado Excluir ponto a ponto, se houver conflito D-D, não processaremos nenhuma alteração, basta registrá-lo. Se houver conflito D-D, então não processaremos nenhuma alteração, basta registrá-la.

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 de inserção, exclusão-inserção ou exclusão de atualização.

Exemplos

Criar publicação no primeiro par (Node1)

Neste exemplo, o script:

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

Criar publicação no segundo par (Node2)

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

Criar assinatura do Node1 para o Node2

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

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