Какую стратегию первичного ключа лучше всего использовать для модели реляционной базы данных, учитывая следующее?
- десятки тысяч пользователей
- несколько клиентов на одного пользователя (телефон, планшет, рабочий стол)
- миллионы строк в таблице (постоянно растущие)
Azure SQL будет центральным хранилищем данных, которое будет отображаться через Web API. Клиенты будут включать в себя веб-приложение и ряд собственных приложений, включая iOS, Android, Mac, Windows 8 и т.д. Веб-приложение потребует "всегда включенное" соединение и не будет иметь локального хранилища данных, но вместо этого будет получать и обновлять через api - думаю CRUD через RESTful API.
Все остальные клиенты (телефон, планшет, рабочий стол) будут иметь локальный db (SQLite). При первом использовании этого типа клиента пользователь должен аутентифицироваться и синхронизироваться. После аутентификации и синхронизации эти клиенты могут работать в автономном режиме (создание, удаление и обновление записей в локальном SQLite db). Эти изменения в конечном итоге будут синхронизироваться с бэкэндом Azure.
Распределенный характер баз данных оставляет нам проблему с первичным ключом и причину возникновения этого вопроса.
Вот что мы рассмотрели до сих пор:
GUID
Каждый клиент создает свои собственные ключи. При синхронизации есть очень небольшой шанс для дублирующего ключа, но нам нужно будет учесть его, написав функциональность в каждом клиенте, чтобы обновить все отношения с помощью нового ключа. GUID являются большими, и когда рассматриваются несколько внешних ключей на таблицу, хранилище может стать проблемой с течением времени. Вероятно, самая большая проблема заключается в случайном характере GUID, что означает, что они не могут (или не должны) использоваться как кластеризованный индекс из-за фрагментации. Это означает, что нам нужно создать кластерный индекс (возможно, произвольный) для каждой таблицы.
Идентичность
Каждый клиент создает свои собственные первичные ключи. При синхронизации эти ключи заменяются сгенерированными сервером ключами. Это добавляет дополнительную сложность процессу синхронизации и заставляет каждого клиента "исправлять" свои ключи, включая все внешние ключи в связанных таблицах.
Composite
Каждому клиенту присваивается идентификатор клиента при первой синхронизации. Этот идентификатор клиента используется в сочетании с локальным автоматически увеличивающимся идентификатором в качестве составного первичного ключа для каждой таблицы. Этот составной ключ будет уникальным, поэтому не должно быть конфликтов при синхронизации, но это означает, что для большинства таблиц потребуется составной первичный ключ. Здесь важны производительность и сложность запросов.
HiLo (объединенный композитный)
Как и композитный подход, каждому клиенту присваивается идентификатор клиента (int32) при первой синхронизации. Идентификатор клиента объединяется с уникальным локальным идентификатором (int32) в один столбец, чтобы сделать уникальный уникальный идентификатор приложения (int64). Это не должно приводить к конфликтам во время синхронизации. Хотя для этих ключей и GUID больше заказов, поскольку идентификаторы, созданные каждым клиентом, являются последовательными, будут тысячи уникальных идентификаторов клиентов, так что мы все еще подвергаем риску фрагментации нашего кластерного индекса?
Мы что-то пропускаем? Есть ли еще какие-то подходы, которые стоит исследовать? Обсуждение плюсов и минусов каждого подхода было бы весьма полезным.