Подтвердить что ты не робот

Стратегия первичного ключа для Android (распределенного приложения)

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

Каков наилучший подход для генерации первичных ключей, которые вы используете в распределенных средах?. По аналогичному вопросу см. . Какая лучшая стратегия первичного ключа для интерактивное/автономное многоклиентское мобильное приложение с SQLite и база данных Azure SQL в качестве центрального магазина?

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

Я не хочу иметь составной идентификатор с идентификатором устройства (также сервер является устройством) и локальным идентификатором. Такой подход не будет работать в любом случае, поскольку сервер должен иметь возможность создавать записи для определенного клиента. В этом случае мне придется использовать идентификатор устройства также на сервере.

Поэтому моим текущим фаворитом является создание моего ключа с длинным типом данных (я сделал это раньше в другом проекте). Я думаю, что буду использовать подход высокий/низкий (см. Здесь здесь Что такое алгоритм Hi/Lo?) и иметь ключ, который состоит из из:

  • идентификатор клиента (например, ~ 28 бит), сгенерированный с сервера
  • низкое значение (например, ~ 4 бит), увеличенное на клиенте, никогда не сохранялось
  • высокое значение (например, ~ 32 бита), увеличенное на клиенте, сохраняется только на клиенте

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

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

Каков наилучший подход для получения высокого идентификатора на Android? Ключ автоинкремента не является решением. Мне нужно что-то вроде функции генератора. И он должен выполняться внутри собственной транзакции (а не "пользовательской" транзакции). Кто-нибудь знает об этом подходе на Android и может ли кто-нибудь указать мне в правильном направлении? (Я нашел этот ответ).

Какую ключевую стратегию вы используете для своего многопроцессорного приложения (онлайн и офлайн)?

4b9b3361

Ответ 1

Это больше вопросов, чем ответов...

Это облегчает работу, если вы можете автоматически генерировать все свои идентификаторы, поэтому вам не нужно извлекать их с сервера и беспокоиться о том, есть ли у вас соединение. Вы отмечаете, что вы не можете использовать общий подход (UUID или ANDROID_ID), потому что вы будете использовать длинный "как это было предложено платформой Android".

Вы имеете в виду тот факт, что Android предполагает, что ваши таблицы SQLite будут иметь длинный первичный ключ _id?

Используете ли вы хранилище данных или базу данных SQL на своем сервере?

Если вы используете хранилище данных с иерархическими ключами (например, google datastore), то как насчет использования UUID/ANDROID_ID в качестве идентификатора клиента, а затем длинного идентификатора элемента данных. Затем на клиенте вы просто храните длинный, а на сервере ваши объекты сохраняются с ключом пути UUID/long.

Почему вы пишете, что "высокий идентификатор должен быть уникальным значением по базе данных"? Поскольку он добавлен с идентификатором клиента, возможно, вы имеете в виду, что он должен быть уникальным в локальной базе данных?

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

Есть ли у ваших сущностей какой-то другой ключевой материал, чтобы вы могли генерировать 32-битный хэш из этого материала для вашего высокого id? Предполагая, что высокий идентификатор должен быть уникальным для конкретного клиента (и предположим, что у вас не будет массивного количества сущностей на клиенте), то я думаю, что вы никогда не столкнетесь с конфликтом, если у вас есть достойный ключевой материал и используйте хэш функция, которая минимизирует столкновения.

Ответ 2

Из моего опыта: использовать локальные идентификаторы на устройстве и отдельные идентификаторы на сервере. Каждый раз, когда вы передаете данные по проводу, конвертируйте их из одного в другое. Это фактически прояснит процесс и облегчит отладку. Процедуры преобразования остаются небольшими, хорошо изолированы и представляют собой естественный элемент архитектуры приложения. Во всяком случае, данные, перемещающиеся по проводу, будут относительно небольшими, и конвертация ID не будет большой накладной. Кроме того, объем данных, хранящихся на мобильном устройстве, является, по-видимому, небольшим (объем находится на сервере).

Я предлагаю преобразовать на устройство с помощью простой таблицы local_ID server_ID. Сервер должен предоставить только одну процедуру: создать пакет ключей, например 444 новых ключа, которые, предположительно, мобильное устройство затем назначит своим локальным идентификаторам и отправит данные на сервер только с идентификаторами server_ID. Таблицу преобразования можно иногда удалять из неиспользуемых идентификаторов, а локальные идентификаторы могут быть повторно использованы, 32-разрядные целые числа, безусловно, будут достаточными.

Мотивация

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

У меня было приложение, которое регенерирует все идентификаторы для каждого файла сохранения и загрузки файлов. Это было неожиданно просто, быстро и открыло элегантные другие возможности, такие как дефрагментация и консолидация ID-пространства.

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

Ответ 3

Я предложил две щедрости по этому вопросу и не нашел ответа, который я ищу. Но я потратил некоторое время на размышления о лучшем решении, и, возможно, вопрос был недостаточно открытым и сосредоточен на решении, которое я имел в виду.

Однако существует много различных доступных стратегий, теперь (после второй щедрости), я думаю, что первый вопрос, на который нужно ответить, - это какие модели данных у вас есть в вашей распределенной среде? Возможно, у вас

  • та же модель (или подмножество) данных на клиенте и сервере
  • модель клиентских данных differnet и модель данных сервера

Если вы ответите с помощью 1), вы можете выбрать для своей ключевой стратегии

  • с использованием GUID
  • используя мой подход Высокий/Низкий
  • отображать ключи как @user3603546, предлагаемые

Если вы ответите с помощью 2), тогда мне приходит в голову только следующее:

  • составной идентификатор

Мне никогда не нравился составной идентификатор, но когда я думаю об этом (и не называю его составным идентификатором в любом случае), это может быть возможным решением. После этого я хочу набросать это решение:

Глоссарий:

  • < клиентский ключ > ... первичный ключ, созданный на стороне клиента, поэтому клиент выбирает реализацию (long _id для Android).
  • < серверный ключ > ... первичный ключ, сгенерированный на стороне сервера, поэтому сервер выбирает реализацию
  • < client id > ... ID для идентификации клиента
  • < device id > ... ID для идентификации устройства, существует 1-n отношение между клиентом и устройством.

Решение:

  • Используйте его только в том случае, если у вас есть модель данных клиента и модель данных сервера.
  • Модель данных клиента имеет поля
    • < клиентский ключ > первичный ключ
    • < серверный ключ > поле с нулевым значением.
  • Модель данных сервера имеет поля
    • < серверный ключ > как первичный ключ
    • < клиентский ключ > поле с нулевым значением.
    • < client id > как обязательное поле данных для различения клиента
  • При синхронизации от сервера к клиенту генерируйте отсутствующий < клиентский ключ > на клиенте и помечать запись как грязную (так что идентификатор клиента приходит на сервер в конце дня)
  • При синхронизации с клиентом на сервер генерируйте отсутствующий < серверный ключ > на сервере перед сохранением.
  • Отображение между моделью данных клиента и сервера может обрабатываться специализированными инфраструктурами, такими как dozer или Orika, однако генерация ключа должна быть интегрирована при выполнении сопоставления.

Мне никогда не нравилось это решение, потому что я всегда думал о терминах данных серверных данных. У меня есть объекты, которые живут только на сервере, и я всегда хотел создать эти объекты на клиенте, что было бы невозможно. Но когда я думаю, что в модели данных клиента у меня может быть одна сущность, например. Продукт, который выводит на сервер два объекта (Product и ClientProduct).

Ответ 4

Посмотрим, получится ли я прямо: вам нужен 32-разрядный номер, уникальный для устройства? Ok:

  • Создайте номер либо произвольно, либо путем хэширования текущего нанотима. Это даст вам довольно уникальную строку, как есть.
  • Затем спросите сервер, если этот номер уже используется. Если он есть, сгенерируйте номер снова и спросите еще раз.

Если вы наносите нанотим, это практически невозможно (не совсем невозможно, сопротивление столкновению не является доказательством столкновения), чтобы получить тот же номер. Учитывая остальную часть вашей строки, это сделает ее совершенно уникальной. Этот метод не требует взаимодействия с сервером, пока ему не понадобится использовать сервер. Скажем, клиент не подключается при первом запуске: сгенерируйте номер, сохраните его, и когда он подключится, прежде чем что-либо еще произойдет, проверьте, существует ли устройство. Если это так, начните с нуля. Таким образом, вы можете получить поистине уникальный идентификатор устройства.

Ответ 5

Невозможно точно знать, что эти ключи, которые вы генерируете на клиенте, уникальны на сервере DB до тех пор, пока вы не свяжетесь с сервером.

Если вы связываетесь с сервером раньше, перед созданием каких-либо записей на стороне клиента, вы можете зарезервировать ряд ключей на сервере. Например, сервер может раздавать ключи в партиях по 10 000. Клиент связывается с сервером и резервирует начало следующей партии доступных ключей, скажем, 60 000. Затем клиент может создавать записи с идентификаторами от 60 000 до 69 999. Как только у клиента заканчиваются ключи, он должен запросить новый диапазон ключей. Если все клиенты и сервер резервируют ключи для себя таким образом, то все созданные идентификаторы будут уникальными в базе данных сервера.

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

Я не уверен, почему вы также пытаетесь включить идентификатор клиента в ключ; сервер назначает высокое значение, и этого достаточно, чтобы получить уникальные ключи, созданные на клиенте.