Condividi tramite


Configurare il rilevamento e la risoluzione dei conflitti dell'ultima scrittura

A partire da SQL Server 2019 (15.x) CU 13, puoi configurare la replica peer-to-peer per risolvere automaticamente i conflitti consentendo l'inserimento o l'aggiornamento più recente per risolvere il conflitto. Se una delle due operazioni di scrittura elimina la riga, SQL Server consente all'eliminazione di vincere il conflitto. Questo metodo è noto come priorità dell’ultima scrittura.

Usa le stored procedure per configurare le priorità dell'ultima scrittura. Non usare la procedura guidata della topologia peer-to-peer per aggiungere o rimuovere nodi quando si usa l'ultima scrittura.

Considerazioni importanti sulla configurazione

Nota

Dopo l'aggiornamento a SQL Server 2019 (15.x) CU13 o una versione successiva, quando configuri una pubblicazione con risoluzione dei conflitti impostata sulle priorità dell’ultima scrittura, nella pubblicazione vengono inclusi metadati aggiuntivi. Se in seguito si disinstalla/effettua il downgrade a una versione inferiore a SQL Server 2019 (15.x) CU13, questi metadati aggiuntivi causeranno problemi. Consigliamo di eliminare tali pubblicazioni prima del downgrade e ricrearle nella versione precedente.

Quando configuri la replica peer-to-peer con individuazione e risoluzione automatica dei conflitti come priorità dell'ultima scrittura, includi le seguenti configurazioni e impostazioni:

  • Crea la pubblicazione con i seguenti parametri:

    • Imposta @p2p_conflictdetection_policy = 'lastwriter' per specificare le priorità dell'ultima scrittura. Vedi sp_addpublication (Transact-SQL). Questo parametro è stato introdotto in SQL Server 2019 (15.x) CU 13. Il valore originatorid predefinito risolve i conflitti in base all'ID origine e corrisponde alla risoluzione del conflitto prima di SQL Server 2019 (15.x) CU 13.
    • Imposta @p2p_continue_onconflict = 'true' per consentire all'agente di distribuzione di risolvere il conflitto.
  • Quando aggiungi l'articolo (sp_addarticle), verifica il comportamento del tipo di comando per il comando di aggiornamento (@upd_cmd). Le opzioni includono:

    • CALL (impostazione predefinita)
    • SCALL

    Vedi i dettagli in sp_addarticle (Transact-SQL).

  • Quando aggiungi un articolo (sp_addarticle) in una pubblicazione con un criterio di rilevamento dei conflitti dell’ultima scrittura, usa CALL o SCALL come tipo di comando per il parametro @upd_cmd, CALL è predefinito.

    Nota

    SQL Server supporta SCALL per @upd_cmd. Con SCALL, quando una transazione aggiorna un valore allo stesso valore, esso non viene considerato come una modifica e il formato di SCALL non fornisce il valore per le colonne che non vengono aggiornate o modificate. Per informazioni dettagliate sul formato di chiamata SCALL, vedi la sintassi di chiamata per le stored procedure.

  • Puoi usare la pubblicazione peer-to-peer con il rilevamento e la risoluzione del conflitto dell'ultima scrittura in un gruppo di disponibilità. Vedere:

  • Puoi visualizzare il conflitto e la relativa risoluzione.

    • In SQL Server Management Studio, fai clic con il pulsante destro del mouse sulla pubblicazione e seleziona Visualizza conflitti.

    O

  • I conflitti di inserimento e aggiornamento vengono risolti in base all'ultima scrittura, ma l'eliminazione prevale sempre. Ad esempio, se si verifica un conflitto di eliminazione-aggiornamento e l'aggiornamento è stato eseguito in un secondo momento, l'eliminazione continua a prevalere.

  • Il rilevamento e la risoluzione dei conflitti dell'ultima scrittura sono determinati in base a una colonna $sys_mw_cd_id nascosta. Il tipo di dati di questa colonna è datetime2.

Confronto tra il rilevamento dei conflitti

Nella tabella seguente viene confrontato il modo in cui vengono rilevati e risolti i conflitti con la replica peer-to-peer tradizionale e quando è abilitata la risoluzione dei conflitti in base all'ultima scrittura:

Tipo di conflitto Dettagli del conflitto Peer-to-peer Ultima scrittura
Inserimento-Inserimento Tutte le righe di ogni tabella che partecipa alla replica peer-to-peer sono identificate in modo univoco tramite valori di chiave primaria. Un conflitto di tipo inserimento-inserimento si verifica quando viene inserita una riga con lo stesso valore di chiave in più di un nodo. Se la riga in ingresso ha la priorità, aggiorniamo la riga di destinazione. In entrambi i casi, le informazioni vengono registrate. Se la riga in ingresso ha la priorità, aggiorniamo la riga di destinazione. In entrambi i casi, le informazioni vengono registrate.
Aggiornamento-Aggiornamento Si verifica quando la stessa riga viene aggiornata in più di un nodo. Se la riga in ingresso ha la priorità, si modificano SOLO le colonne cambiate. Se la riga in ingresso ha la priorità, si modificano tutte le colonne nella destinazione (se @upd_cmd è impostata default su – CALL).
Aggiornamento-Inserimento Si verifica se una riga viene aggiornata in un nodo ma viene anche eliminata e quindi reinserita in un altro nodo. Se la riga in ingresso ha la priorità, si modificano SOLO le colonne cambiate. Ciò si verifica quando una riga viene aggiornata in peer1 e la stessa riga viene eliminata e reinserita nel peer2. Quando si verifica la sincronizzazione, la riga in peer1 viene eliminata perché l'eliminazione prevale sempre e quindi viene inserita la stessa riga, mentre la riga viene aggiornata in peer2 come è avvenuto in un secondo momento. Ciò porterà a una non convergenza.
Inserimento-Aggiornamento Si verifica se una riga è stata aggiornata e poi reinserita in un nodo e la stessa riga è stata aggiornata in un altro nodo. Se la riga in ingresso ha la priorità, aggiorniamo tutte le colonne. Ciò si verifica quando una riga viene eliminata e reinserita in peer1 e la stessa riga viene aggiornata in peer2. Quando si verifica la sincronizzazione, la riga viene eliminata in peer2 perché l'eliminazione prevale sempre e quindi viene nuovamente inserita. In peer1 l'aggiornamento viene ignorato.
Eliminazione-Inserimento

Inserimento-Eliminazione
Si verifica se una riga viene eliminata in un nodo ma viene anche eliminata e quindi reinserita in un altro nodo. Attualmente riteniamo che sia in conflitto D-U e se la riga in ingresso ha la priorità, eliminiamo la riga dalla destinazione. Ciò si verifica quando una riga viene eliminata in peer1 e la stessa riga viene eliminata e reinserita in peer2. Quando si verifica la sincronizzazione, la riga nel peer2 viene eliminata, mentre la riga viene inserita in peer1. Ciò si verifica perché non vengono archiviate informazioni sulla riga eliminata, quindi non si sa se la riga è stata eliminata o non era presente nel peer. Ciò porterà a una non convergenza.
Eliminazione-Aggiornamento Si verifica se una riga viene eliminata in un nodo, ma la stessa riga viene aggiornata in un altro nodo. Attualmente riteniamo che sia in conflitto D-U e se la riga in ingresso ha la priorità, eliminiamo la riga dalla destinazione. Questo è un conflitto D-U. Poiché l'eliminazione ha sempre la priorità, l'eliminazione in ingresso avrà la priorità ed elimineremo la riga dalla destinazione.
Aggiornamento-Eliminazione Si verifica se una riga viene aggiornata in un nodo ma viene anche eliminata in un altro nodo. Nella stored procedure di aggiornamento peer-to-peer, se si verifica un conflitto U-D, stampiamo il messaggio seguente e non lo risolviamo.

An update-delete conflict was detected and unresolved. The row could not be updated since the row does not exist.
Questo è un conflitto U-D. Poiché l'eliminazione ha sempre la priorità, l'aggiornamento in ingresso viene ignorato.
Eliminazione-Eliminazione Si verifica quando una riga viene eliminata in più di un nodo. Nella stored procedure di eliminazione peer-to-peer, se si verifica un conflitto D-D, non viene elaborata alcuna modifica, è sufficiente registrarla. Se si verifica un conflitto D-D, non viene elaborata alcuna modifica, è sufficiente registrarla.

Nota

Nell'implementazione corrente dei criteri di rilevamento dei conflitti dell'ultima scrittura, l'eliminazione ha sempre la priorità quando si verifica un conflitto di inserimento-eliminazione, eliminazione-inserimento, inserimento o aggiornamento-eliminazione.

Esempi

Crea una pubblicazione nel primo peer (Node1)

In questo esempio, lo script:

  • Pubblica un database denominato MWPubDB.
  • Assegna il nome PublMW alla pubblicazione.
  • Configura i criteri di rilevamento e risoluzione dei conflitti come priorità dell'ultima scrittura:
    , @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

Crea una pubblicazione nel secondo peer (Node2)

Il seguente script crea la pubblicazione nel secondo peer (Node 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

Crea una sottoscrizione da Node1 a 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

Crea una sottoscrizione da Node2 a 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