Способы обнаружения и разрешения конфликтов, используемые при репликации слиянием

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

Обнаружение и разрешение конфликтов

Агент слияния обнаруживает конфликты, используя столбец lineage системной таблицы MSmerge_contents. Если для статьи включено отслеживание на уровне столбцов, используется также столбец COLV1. Эти столбцы содержат метаданные, указывающие на время вставки либо обновления строки или столбца, а также на то, какие узлы в топологии репликации слиянием вносили изменения в строку или столбец. Для просмотра этих метаданных можно использовать системную хранимую процедуру sp_showrowreplicainfo (Transact-SQL).

Когда агент слияния перечисляет изменения, которые необходимо применить во время синхронизации, он сравнивает эти метаданные для каждой строки на издателе и подписчике. Агент слияния использует эти метаданные, чтобы определить, были ли внесены изменения в строку или столбец более чем на одном узле в топологии, что указывает на потенциальный конфликт. После обнаружения конфликта агент слияния запускает арбитр конфликтов, указанный для статьи, имеющей конфликт, и использует этот арбитр для определения победителя в конфликте. Победившая строка применяется на издателе и подписчике, а данные из проигравшей строки записываются в таблицу конфликтов.

Агент слияния разрешает конфликты автоматически и немедленно, если только для статьи не выбрано интерактивное разрешение конфликтов. Дополнительные сведения см. в разделе Интерактивное разрешение конфликтов. Если победившая в конфликте строка изменяется вручную с помощью средства просмотра конфликтов репликации слиянием, агент слияния применяет победившую версию на проигравшем сервере во время следующей синхронизации.

Запись успешно разрешенных конфликтов в журнал

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

  • Для конфликтов UPDATE и INSERT он записывает проигравшую версию строки в таблицу конфликтов для статьи, имя которой имеет вид conflict_<ИмяПубликации>_<ИмяСтатьи>. Общие сведения о конфликте (например, тип конфликта) записываются в таблицу MSmerge_conflicts_info.

  • Для конфликтов DELETE он записывает проигравшую версию строки в таблицу MSmerge_conflicts_info. Когда удаление проигрывает обновлению, в проигравшей строке данные отсутствуют (поскольку проиграло удаление), поэтому в таблицу conflict_<ИмяПубликации>_<ИмяСтатьи> ничего не записывается.

Таблицы конфликтов для каждой статьи создаются в базе данных публикации, базе данных подписки или в обеих базах (по умолчанию) в зависимости от значения, заданного для параметра @conflict_logging процедуры sp_addmergepublication. Каждая таблица конфликтов имеет такую же структуру, как и статья, на которой она основана, с добавлением столбца origin_datasource_id. Агент слияния удаляет данные из таблицы конфликтов, если их возраст превышает срок хранения конфликтов для публикации, который задается с помощью параметра @conflict_retention процедуры sp_addmergepublication (по умолчанию срок хранения равен 14 дням).

Репликация предоставляет средство просмотра конфликтов репликации и хранимые процедуры (sp_helpmergearticleconflicts, sp_helpmergeconflictrows и sp_helpmergedeleteconflictrows) для просмотра данных конфликтов. Дополнительные сведения см. в разделах Как просмотреть и разрешить конфликты данных для публикации слиянием (среда SQL Server Management Studio) и Как просмотреть сведения о конфликтах для публикаций слиянием (программирование репликации на языке Transact-SQL).

Факторы, влияющие на разрешение конфликтов

Имеются два фактора, которые влияют на способ разрешения конфликта, обнаруженного агентом слияния.

  • тип подписки: клиентская или серверная (на разрешение конфликтов не влияет на то, является ли подписка подпиской по запросу или принудительной подпиской);

  • используемый тип отслеживания конфликтов: на уровне строк, на уровне столбцов или на уровне логических записей.

Типы подписок

При создании подписки помимо указания того, является ли она принудительной подпиской или подпиской по требованию, задается ее тип — клиентская или серверная. После создания подписки этот тип изменить нельзя (в предыдущих версиях Microsoft SQL Server клиентские и серверные подписки назывались соответственно локальными и глобальными подписками).

Подписка с присвоенным значением приоритета (от 0,00 до 99,99) называется серверной подпиской. Подписка, использующая значение приоритета издателя, называется клиентской подпиской. Кроме этого, подписчики с серверными подписками могут переиздавать данные на другие подписчики. В приведенной ниже таблице содержится сводка основных отличий и применений каждого типа подписчиков.

Тип

Значение приоритета

Применение

Сервер

Назначается пользователем

Когда разные подписчики должны иметь разные приоритеты.

Клиент

0,00, но изменения данных после синхронизации принимают значения приоритета издателя

Когда все подписчики должны иметь одинаковый приоритет, и когда первый подписчик, производящий слияние с издателем, должен победить в конфликте.

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

При изменении строки или столбца в серверной подписке приоритет подписки сохраняется в метаданных изменений. Данное значение приоритета передается вместе с измененной строкой при ее слиянии с изменениями от других подписчиков. Это гарантирует, что изменения, внесенные подпиской с более высоким приоритетом, не проиграют последующим изменениям, внесенным подпиской с более низким приоритетом.

Подписка не может иметь явного значения приоритета, превышающего значение приоритета издателя. Издатель высшего уровня в топологии репликации слиянием всегда имеет явное значение приоритета, равное 100,00. Все подписки на эту публикацию должны иметь значение приоритета, меньше этого значения. В переиздающей топологии:

  • если подписчик переиздает данные, подписка должна быть серверной подпиской со значением приоритета меньшим, чем у издателя, находящегося выше подписчика;

  • если подписчик не переиздает данные (поскольку он находится на конечном уровне дерева переиздания), подписка должна быть клиентской подпиской.

Дополнительные сведения о серверных подписках и приоритетах см. в разделе Пример разрешения конфликта слияния, основанного на типе подписки и присвоенных приоритетах.

Задержка уведомления о конфликте

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

  1. Издатель и подписчик с более низким приоритетом, имеющий имя LowPrioritySub, обмениваются изменениями в течение нескольких синхронизаций без конфликтов.

  2. Подписчик с более высоким приоритетом, имеющий имя HighPrioritySub, не синхронизировался с издателем в течение некоторого времени и внес изменения в те же строки, в которые внес изменения подписчик LowPrioritySub.

  3. Подписчик HighPrioritySub синхронизируется с издателем и побеждает в конфликтах между его изменениями и изменениями подписчика LowPrioritySub, поскольку он имеет более высокий приоритет, чем подписчик LowPrioritySub. Издатель теперь содержит изменения, внесенные подписчиком HighPrioritySub.

  4. После этого подписчик LowPrioritySub осуществляет слияние с издателем и загружает большое количество изменений вследствие конфликтов с подписчиком HighPrioritySub.

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

Уровень отслеживания

Является ли изменение данных конфликтом, зависит от типа отслеживания конфликтов, установленного для статьи: на уровне строк, на уровне столбцов или на уровне логических записей. Дополнительные сведения об отслеживании на уровне логических записей см. в разделе Распознавание и разрешение конфликтов в логических записях.

При распознавании конфликтов на уровне строк изменения, внесенные в соответствующие строки, рассматриваются как конфликт, независимо от того, внесены они в один и тот же столбец или нет. Например, предположим, что одно изменение вносится в столбец адреса в строке издателя, а второе изменение вносится в столбец номера телефона соответствующей строки подписчика (в той же таблице). При отслеживании на уровне строк обнаруживается конфликт, поскольку изменения внесены в одну и ту же строку. При отслеживании на уровне столбцов конфликт не обнаруживается, поскольку изменения внесены в разные столбцы в одной и той же строке.

Для отслеживания на уровне строк и на уровне столбцов разрешение конфликтов происходит так же: вся строка данных заменяется данными от победителя конфликта (для отслеживания конфликтов на уровне логических записей разрешение конфликта зависит от свойства статьи logical_record_level_conflict_resolution).

Использование того или иного варианта отслеживания обычно определяется семантикой приложения. Например, при обновлении данных клиента, которые обычно вводятся одновременно, таких как адрес и номер телефона, следует выбрать отслеживание на уровне строк. Если в данной ситуации выбрать отслеживание на уровне столбцов, изменения адреса клиента в одном месте и номера телефона клиента в другом месте не будут обнаружены как конфликт: данные будут объединены при синхронизации, и ошибка останется незамеченной. В других ситуациях наиболее логичным выбором может быть обновление отдельных столбцов с различных веб-узлов. Например, два узла могут иметь доступ к разным типам статистических данных о клиенте, таких как уровень доходов и сумма покупок по кредитной карте. Выбор отслеживания на уровне столбцов гарантирует, что оба узла могут вводить статистические данные для разных столбцов, не создавая ненужные конфликты.

ПримечаниеПримечание

Если для приложения не требуется отслеживания на уровне столбцов, рекомендуется использовать отслеживание на уровне строк (по умолчанию), поскольку оно обычно обеспечивает лучшую производительность синхронизации. Если применяется трассировка на уровне строк, базовая таблица может содержать не более 1024 столбцов, но столбцы из статьи должны фильтроваться так, чтобы публикации подверглось не более 246 столбцов. Если применяется трассировка на уровне столбцов, то базовая таблица может содержать не более 246 столбцов.

Типы конфликтов

Несмотря на то, что большинство конфликтов относятся к обновлениям (обновление на одном узле конфликтует с обновлением или удалением на другом узле), существуют и другие типы конфликтов. Конфликт каждого типа, описываемого в данном разделе, может возникать во время фазы выгрузки или фазы загрузки обработки слияния. Обработка передачи является первым согласованием изменений, выполняемым в конкретном сеансе слияния, и представляет собой фазу, во время которой агент слияния реплицирует изменения с подписчика на издатель. Конфликты, обнаруженные во время этой обработки, называются конфликтами передачи. Обработка загрузки включает в себя перемещение изменений от издателя подписчику и происходит после обработки передачи. Конфликты, возникающие во время этой фазы обработки, называются конфликтами загрузки.

Дополнительные сведения о типах конфликтов см. в разделе MSmerge_conflicts_info (Transact-SQL), особенно сведения о столбцах conflict_type и reason_code.

Конфликты «обновление-обновление»

Агент слияния обнаруживает конфликты «обновление-обновление», когда обновление строки (или столбца, или логической записи) на одном узле конфликтует с другим обновлением той же строки на другом узле. Действиями арбитра конфликтов по умолчанию в данном случае будут отправка победившей версии строки проигравшему узлу и запись проигравшей строки в таблицу конфликтов статьи.

Конфликты «обновление-удаление»

Агент слияния обнаруживает конфликт «обновление-удаление», когда обновление данных на одном узле конфликтует с удалением на другом. В этом случае агент слияния обновляет строку. Однако когда агент слияния ищет эту строку в месте назначения, он не может найти ее, поскольку она была удалена. Если победителем является узел, который обновил строку, то удаление на проигравшем узле отменяется, а агент слияния отсылает только что обновленную строку проигравшему в конфликте. Агент слияния записывает сведения о проигравшей версии строки в таблицу MSmerge_conflicts_info.

Конфликты неудачных изменений

Агент слияния объявляет об этих конфликтах, когда он не может применить конкретное изменение. Это обычно происходит в связи с различием определений ограничений между издателем, подписчиком и использованием свойства NOT FOR REPLICATION (NFR) для ограничения. Примеры включают следующее.

  • Конфликт внешнего ключа на подписчике, который может произойти, когда ограничение со стороны подписчика не помечено как NFR.

  • Различия ограничений между издателем и подписчиком, при этом ограничения не отмечены как NFR.

  • Недоступность зависимых объектов на подписчике. Например, при публикации представления, но не таблицы, от которой зависит это представление, возникает сбой при попытке вставки посредством этого представления на подписчике.

  • Логика фильтра соединения для публикации, которая не соответствует ограничениям первичного ключа и внешнего ключа. Конфликты могут возникать, когда реляционный механизм SQL Server пытается соблюсти ограничение, но агент слияния соблюдает определение фильтра соединения между статьями. Агент слияния не может применить изменения на целевом узле из-за ограничений уровня таблиц, что приводит к конфликту.

  • Конфликты, связанные с нарушениями уникальных индексов, ограничений уникальности или нарушений первичного ключа, могут возникать, если для статьи определены столбцы идентификаторов, а автоматическое управление идентификаторами не используется. Это может привести к проблемам, если два подписчика должны использовать одно и то же значение идентификатора для вновь вставляемой строки. Дополнительные сведения об управлении диапазонами идентификаторов см. в разделе Репликация столбцов идентификаторов.

  • Конфликты, связанные с логикой триггеров, не дающей агенту слияния вставить строку в целевую таблицу. Рассмотрим триггер Update, определенный на подписчике; триггер не отмечен как NFR и содержит в своей логике команду ROLLBACK. При возникновении сбоя триггер выдает команду ROLLBACK в отношении транзакции, что приводит к обнаружению агентом слияния конфликта, вызванного неудачными изменениями.