Partager via


Configurer la détection & la résolution des conflits de dernière écriture

À compter de SQL Server 2019 (15.x) CU13, vous pouvez configurer la réplication d’égal à égal pour résoudre automatiquement les conflits en autorisant l’insertion ou la mise à jour la plus récente pour gagner le conflit. Si l’une des écritures supprime la ligne, SQL Server autorise la suppression à gagner le conflit. Cette méthode est connue sous le nom de dernière victoire d’écriture.

Utilisez des procédures stockées pour configurer les dernières victoires d’écriture. N’utilisez pas l’Assistant Topologie d’égal à égal pour ajouter ou supprimer des nœuds lorsque vous utilisez le dernier enregistreur.

Considérations importantes relatives à la configuration

Note

Une fois que vous avez effectué la mise à jour vers SQL Server 2019 (15.x) CU13 ou une version ultérieure, lorsque vous configurez une publication avec la résolution de conflit définie sur la dernière victoire d’écriture, des métadonnées supplémentaires sont incluses dans la publication. Si vous désinstallez/rétrogradez ultérieurement vers une version antérieure à SQL Server 2019 (15.x) CU13, ces métadonnées supplémentaires provoquent des problèmes. Vous devez supprimer toutes ces publications avant la rétrogradation, puis les recréer sur la version inférieure.

Lorsque vous configurez la réplication d’égal à égal avec la détection et la résolution automatiques des conflits pour résoudre le dernier résultat d’écriture, incluez les configurations et paramètres suivants :

  • Créez la publication avec les paramètres suivants,

    • Définissez @p2p_conflictdetection_policy = 'lastwriter' pour spécifier les derniers gains d’écriture. Voir sp_addpublication (Transact-SQL). Ce paramètre est introduit dans SQL Server 2019 (15.x) CU13. La valeur originatorid par défaut résout le conflit en fonction de l’ID d’origine et est identique à la résolution des conflits avant SQL Server 2019 (15.x) CU13.

    • Définissez @p2p_continue_onconflict = 'true' pour autoriser l’agent de distribution à résoudre le conflit.

  • Lorsque vous ajoutez l’article (sp_addarticle), confirmez le comportement du type de commande pour la commande de mise à jour (@upd_cmd). Les options sont les suivantes :

    • CALL (Par défaut)
    • SCALL

    Consultez les détails de sp_addarticle.

  • Lorsque vous ajoutez un article (sp_addarticle) dans une publication avec une stratégie de détection de conflit du dernier enregistreur, utilisez CALL ou SCALL comme type de commande pour @upd_cmd le paramètre, CALL est la valeur par défaut.

    Note

    SQL Server prend en charge SCALL pour @upd_cmd. Avec SCALL, lorsqu’une transaction met à jour une valeur sur la même valeur, elle n’est pas considérée comme une modification et SCALL un format ne fournit pas la valeur des colonnes qui ne sont pas mises à jour ou modifiées. Pour plus d’informations sur le format d’appel SCALL, consultez la syntaxe des appels pour les procédures stockées.

  • Vous pouvez utiliser la publication d’égal à égal avec la détection et la résolution des conflits du dernier enregistreur dans un groupe de disponibilité. Voir:

  • Vous pouvez voir le conflit et sa résolution.

    • Dans SQL Server Management Studio, cliquez avec le bouton droit sur la composition, puis sélectionnez Afficher les conflits.

    ou

  • Les conflits d’insertion et de mise à jour sont résolus en fonction du dernier enregistreur, mais la suppression prévaut toujours. Par exemple, si vous avez un conflit de suppression-mise à jour et que la mise à jour a été effectuée ultérieurement, la suppression gagne toujours.

  • La détection et la résolution des conflits du dernier enregistreur sont déterminées en fonction d’une colonne $sys_mw_cd_idmasquée. Le type de données de cette colonne est datetime2.

Comparaison de la détection des conflits

Le tableau suivant compare la façon dont les conflits sont détectés et résolus avec la réplication d’égal à égal classique et lorsque la résolution des conflits du dernier enregistreur est activée :

Type de conflit Détails des conflits Pair à pair Dernier enregistreur
Insert-Insert Toutes les lignes de chaque table participant à la réplication d’égal à égal sont identifiées de manière unique à l’aide de valeurs de clé primaire. Un conflit d’insertion se produit lorsqu’une ligne avec la même valeur de clé a été insérée à plusieurs nœuds. Si la ligne entrante est le gagnant, nous mettons à jour la ligne de destination. Dans les deux cas, nous enregistrons les informations. Si la ligne entrante est le gagnant, nous mettons à jour la ligne de destination. Dans les deux cas, nous enregistrons les informations.
Update-Update Se produit lorsque la même ligne a été mise à jour à plusieurs nœuds. Si la ligne entrante est le gagnant, nous modifions UNIQUEMENT les colonnes modifiées. Si la ligne entrante est le gagnant, nous modifions toutes les colonnes à la destination (si @upd_cmd elle est définie sur defaultCALL).
Update-Insert Se produit si une ligne a été mise à jour à un nœud, mais que la même ligne a été supprimée, puis réinsérée sur un autre nœud. Si la ligne entrante est le gagnant, nous modifions UNIQUEMENT les colonnes modifiées. Cela se produit lorsqu’une ligne est mise à jour sur peer1 et que la même ligne est supprimée et réinsérée sur peer2. Lorsque la synchronisation se produit, la ligne sur peer1 laquelle la suppression est supprimée gagne toujours, puis la même ligne est insérée, tandis que la ligne est mise à jour à peer2 mesure que la mise à jour s’est produite ultérieurement. Cela entraîne une non-convergence.
Insertion - Mise à jour Se produit si une ligne a été supprimée, puis réinsérée à un nœud et que la même ligne a été mise à jour sur un autre nœud. Si la ligne entrante est le gagnant, nous mettons à jour toutes les colonnes. Cela se produit lorsqu’une ligne est supprimée et réinsérée sur peer1 et que la même ligne est mise à jour sur peer2. Lorsque la synchronisation se produit, la ligne est supprimée en peer2 tant que suppression gagne toujours, puis elle est insérée à nouveau. Sur peer1, la mise à jour est ignorée.
Delete-Insert

Insert-Delete
Se produit si une ligne a été supprimée à un nœud, mais que la même ligne a été supprimée, puis réinsérée sur un autre nœud. Nous pensons actuellement qu’il s’agit d’un conflit D-U et si la ligne entrante est alors gagnante, nous supprimons la ligne de destination. Cela se produit lorsqu’une ligne est supprimée peer1 et que la même ligne est supprimée + réinsertée sur peer2. Lorsque la synchronisation se produit, la ligne activée peer2 est supprimée, tandis que la ligne est insérée sur peer1. Cela se produit parce que nous ne stockons pas d’informations sur la ligne supprimée. Nous ne savons donc pas si la ligne a été supprimée ou n’a pas été présente sur l’homologue. Cela entraîne une non-convergence.
Delete-Update Se produit si une ligne a été supprimée à un nœud, mais que la même ligne a été mise à jour sur un autre nœud. Nous pensons actuellement qu’il s’agit d’un conflit D-U et si la ligne entrante est le gagnant, nous supprimons la ligne de la destination. Il s’agit d’un conflit D-U. Comme la suppression gagne toujours, la suppression entrante est le gagnant et nous supprimons la ligne de destination.
Update-Delete Se produit si une ligne a été mise à jour à un nœud, mais que la même ligne a été supprimée à un autre nœud. Dans la procédure stockée de mise à jour d’égal à égal, s’il existe un conflit U-D, nous affichons le message suivant et ne le résolvez pas.

An update-delete conflict was detected and unresolved. The row could not be updated since the row does not exist.
Il s’agit d’un conflit U-D. Comme la suppression gagne toujours, la mise à jour entrante est ignorée.
Delete-Delete Se produit lorsqu’une ligne a été supprimée à plusieurs nœuds. Dans la procédure stockée Delete d’égal à égal, s’il existe un conflit D-D, nous ne traitons aucune modification, il vous suffit de l’enregistrer. S’il y a un conflit D-D, nous ne traitons aucune modification, il vous suffit de l’enregistrer.

Note

Dans l’implémentation actuelle de la stratégie de détection de conflit du dernier enregistreur, la suppression gagne toujours lorsqu’il existe un conflit insert-delete, delete-insert ou update-delete.

Examples

Créer une publication sur le premier homologue (Node1)

Dans cet exemple, le script :

  • Publie une base de données appelée MWPubDB.
  • Nomme la publication PublMW.
  • Configure la stratégie de détection et de résolution des conflits en tant que dernière écriture gagnée : , @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

Créer une publication sur un deuxième homologue (Node2)

Le script suivant crée la publication sur le deuxième homologue (nœud 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

Créer un abonnement de Node1 à 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

Créer un abonnement de 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