Устранение конфликтов первичных ключей в клиентской базе данных при синхронизации с сервером

Вопрос

Как избежать конфликтов первичных ключей в клиент-серверных базах данных

Фон

Я синхронизирую несколько баз данных друг с другом. У меня есть одна центральная база данных SQL Server и множество клиентских баз данных SQL Server. Теперь скажем, что у меня есть таблица X в базе данных, у которой есть идентификатор первичного ключа.

В первом клиенте у нас есть следующие идентификаторы в таблице X:

X (клиент 1)
--------
1|SomeValue
2|SomeValue
3|SomeValue
4|SomeValue

а во втором клиенте у меня

X (клиент 2)
--------
1|SomeValue
2|SomeValue
3|SomeValue
4|SomeValue

Когда я синхронизирую, я хочу, чтобы клиенты только загружали свои данные, а не загружали их.

Теперь, когда я синхронизирую первый клиент с сервером, он добавит первичные ключи из (1..4). Однако, когда я синхронизируюсь со вторым клиентом, будет PRIMARY KEY CLASH. Как я могу решить эту проблему?

Я использую SQL Server 2008 R2, Sync Framework и C#.

Я уже рассматривал идею использования GUID в качестве первичного ключа, что в моем случае неприемлемо, поскольку я имею дело с устаревшей базой данных. Кроме того, идея повторного заполнения значения IDENTITY несколько подвержена ошибкам. Как я могу увеличить столбец идентификаторов без вставки значения? PS: первичные ключи установлены как IDENTITY с шагом 1.


person Saleh Omar    schedule 05.01.2013    source источник
comment
С какой целью вы полагаетесь на сгенерированные клиентом первичные ключи?   -  person Simon Whitehead    schedule 05.01.2013
comment
Я собираю информацию из разных городов одновременно. После этого данные загружаются в таблицу, которая находится на сервере в столице. База данных используется в приложении для опросов, что означает, что у меня есть разные планшеты для опросов, и все они имеют локальную базу данных. После сбора информации пришло время отправить их на сервер. Таким образом, может случиться так, что первичный ключ в одной локальной базе данных имеет то же значение, что и первичный ключ в другой базе данных. Я надеюсь, что это более ясно.   -  person Saleh Omar    schedule 05.01.2013
comment
Можете ли вы внести какие-либо изменения в устаревшую базу данных?   -  person Shane Charles    schedule 05.01.2013
comment
Изменение устаревшей базы данных — это большое «нет-нет» для меня.   -  person Saleh Omar    schedule 05.01.2013


Ответы (3)


Для поддержки распределенных клиентов, способных вставлять записи, схема должна поддерживать клиентские БД, создающие строки без конфликтов. Это означает либо использование GUID для PK, либо объединенный ключ. (ID + ClientID) *в любом случае, изменение схемы.

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

person Steve Py    schedule 05.01.2013
comment
Я имел в виду идею конкатенации, и, как мне кажется, это наиболее жизнеспособное решение. Надеюсь, мы учимся на своих ошибках для будущих проектов. Спасибо. - person Saleh Omar; 05.01.2013

Есть несколько способов, чтобы это работало. Во-первых, если существует гарантированный уникальный ключ, по которому как вышестоящая, так и нижестоящая базы данных могут ссылаться на эту запись. В случае SQL-сервера тип ключа, который вам нужен, — это UNIQUEIDENTIFIER (также известный как GUID).

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

Третий вариант — вообще не изменять клиентские базы данных, а только добавлять столбец идентификатора местоположения в таблицы в «главной» базе данных. Если вы вручную синхронизируете код, то код должен учитывать, откуда берется запись, и добавлять соответствующий идентификатор. Это может быть сложно, но, безусловно, возможно.

Обнажая, что вам придется повторно заполнить клиентские базы данных, чтобы начать их значения идентификаторов в определенной точке. Например, клиент 1 может получить значения от 1 до 10000, а клиент 2 может получить значения от 10001 до 20000. Однако здесь вы можете увидеть вероятность сбоя. Не только в том случае, если клиент превышает разрешенный диапазон, но и в том случае, если вы настроили дополнительных клиентов, а кто-то испортил начальные значения. Кроме того, было бы полной PITA для исправления всех существующих клиентов.


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

person NotMe    schedule 05.01.2013
comment
Как я вижу, совмещение идентификатора и идентификатора клиента было бы лучшим вариантом. Я предложил использовать GUID своему руководителю, но, как он сказал мне, это не обсуждается. Ведь я только присоединился к проекту с самого конца. Большое спасибо. - person Saleh Omar; 05.01.2013
comment
Привет, я разрабатываю банкомат POS-приложения и борюсь с аналогичной проблемой. У нас есть несколько отдельных клиентских баз данных, в которых регистрируются их собственные продажи, и они должны быть синхронизированы с центральной базой данных. Дополнительная проблема заключается в том, что они также должны иметь возможность редактировать записи, которые были добавлены другими базами данных, например клиентом. Ваш второй вариант кажется наиболее жизнеспособным. Что бы вы порекомендовали? Только сохранение местоположения записи при первоначальном создании, поскольку их можно редактировать в другой базе данных? Я читал, что guids/uuid, по-видимому, являются огромным узким местом, если вам нужно вставить много записей. - person Laurent; 01.02.2019
comment
@Laurent: я могу сказать по опыту, что узкое место, о котором вы читали в отношении GUID, невероятно раздуто. В одной из наших баз данных есть таблица с более чем 100 миллионами записей. Все идентификаторы в этой базе данных являются идентификаторами GUID — все до единого. У нас бывают периоды, когда в день может быть вставлен или обновлен миллион записей, и замедление не происходит. Основной сервер БД имеет 32 ГБ оперативной памяти под управлением SQL 2008 R2. Но если вы просто не хотите использовать GUID, тогда да, сохраните идентификатор местоположения только при создании начальной записи. - person NotMe; 02.02.2019
comment
@NotMe: Спасибо за ответ. Кстати, как бы вы сохранили эти GUID в базе данных? Как двоичный (16) или char (36)? - person Laurent; 05.02.2019
comment
@Лоран. Используемый тип данных называется uniqueidentifier. docs.microsoft.com/en-us/sql/t-sql/типы данных/ - person NotMe; 06.02.2019

в дополнение к третьему варианту @Chris, если вы используете Sync Framework, вы можете «обмануть» его, добавив дополнительный столбец для PK на стороне сервера.

см.: Часть 1. Синхронизация загрузки, когда первичные ключи клиента и сервера различаются

person JuneT    schedule 06.01.2013