Синхронизация баз данных клиент-сервер

Я ищу некоторые общие стратегии для синхронизации данных на центральном сервере с клиентскими приложениями, которые не всегда находятся в сети.

В моем конкретном случае у меня есть приложение для Android с базами данных sqlite и веб-приложение PHP с базой данных MySQL.

Пользователи смогут добавлять и редактировать информацию о приложении телефона и веб-приложении. Мне нужно убедиться, что изменения в одном месте отражаются повсюду, даже когда телефон не может немедленно связаться с сервером.

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

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

Ответ 1

Первое, что вам нужно решить, - это общая политика о том, какая сторона считается "авторитетной" в случае противоречивых изменений.

I.e: предположим, что запись № 125 будет изменена на сервере 5 января в 10 часов вечера, и одна и та же запись будет изменена на одном из телефонов (позвоните клиенту А) 5 января в 11 вечера. Последняя синхронизация была 3 января. Затем пользователь снова подключается, например, 8 января.

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

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

Это решение чрезвычайно важно, и вы должны весить "роль" клиентов. Особенно, если есть потенциальный конфликт не только между клиентом и сервером, но в случае, если разные клиенты могут изменить одну и ту же запись (ы).

[Предполагая, что # 125 может быть изменен вторым клиентом (Клиент B), существует вероятность того, что Клиент B, который еще не синхронизировался, предоставит еще одну версию той же записи, что делает предыдущее разрешение конфликта спорным ]

Относительно "созданной или обновленной" точки выше... как вы можете правильно идентифицировать запись, если она была создана на одном из клиентов (если это имеет смысл в вашей проблемной области)? Предположим, что ваше приложение управляет списком деловых контактов. Если Клиент А говорит, что вы должны добавить недавно созданного Джона Смита, а на сервере есть Джон Смит, созданный вчера клиентом D... вы создаете две записи, потому что не можете быть уверены, что они не разные люди? Вы также попросите пользователя смириться с этим конфликтом?

У клиентов есть "собственность" подмножества данных? То есть если Клиент B настроен как "авторитет" на данные для Области №5, может ли Клиент А изменить/создать записи для Области № 5 или нет? (Это облегчит разрешение конфликтов, но может оказаться неосуществимым для вашей ситуации).

Подводя итог, основные проблемы:

  • Как определить "идентификатор", учитывая, что отдельные клиенты не могли получить доступ к серверу перед созданием новой записи.
  • В предыдущей ситуации, независимо от того, насколько сложное решение может привести к дублированию данных, вы должны предвидеть, как периодически их решать и как информировать клиентов о том, что то, что они считают "Запись № 675", фактически было объединено с/заменено записью # 543
  • Решите, разрешатся ли конфликты с помощью fiat (например, "версия сервера всегда козыряет клиента, если первая была обновлена ​​с момента последней синхронизации" ) или вручную [/li >
  • В случае fiat, особенно если вы решаете, что клиент имеет приоритет, вы также должны позаботиться о том, как обращаться с другими, еще не синхронизированными клиентами, которые могут иметь еще несколько изменений.
  • Предыдущие элементы не учитывают детализацию ваших данных (чтобы упростить описание). Достаточно сказать, что вместо рассуждения на уровне "Запись", как в моем примере, вы можете найти более подходящим для записи изменений на уровне поля. Или для работы над набором записей (например, Запись человека + Запись адреса + Запись контактов) одновременно, рассматривая их совокупность как своего рода "Мета-запись".

Библиография:

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

Из сайта Dr.Dobbs:

  • Создание приложений с SQL Server CE и SQL RDA Биллом Вагнером 19 мая 2004 г. (Рекомендации по разработке приложения для настольного и мобильного ПК - Windows/.NET)

Из arxiv.org:

  • Неконфликтный реплицированный JSON Datatype - в документе описывается реализация JSON CRDT (Реплицированные типы конфликтов - CRDT) - это семейство структуры данных, которые поддерживают одновременную модификацию и гарантируют конвергенцию таких параллельных обновлений).

Ответ 2

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

Наблюдение 1: знать о физических ударах, поскольку строки удаляются из исходного db, и вы должны делать то же самое на сервере db. Вы можете решить это, избегая физических удалений или протоколирования всех удалений в таблице с отметками времени. Что-то вроде этого: DeletedRows = (id, table_name, pk_column, pk_column_value, timestamp) Итак, вы должны прочитать все новые строки таблицы DeletedRows и выполнить удаление на сервере с помощью table_name, pk_column и pk_column_value.

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

Ответ 3

Если кто-то имеет дело с подобной проблемой дизайна и нуждается в синхронизации изменений на нескольких устройствах Android, я рекомендую проверить Google Cloud Messaging для Android (GCM).

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

В принципе, каждый клиент отправляет изменения дельта на сервер. Например. Идентификатор ресурса ABCD1234 изменился со значения 100 до 99.

Сервер проверяет эти изменения дельты на свою базу данных и либо одобряет изменение (клиент находится в синхронизации), либо обновляет свою базу данных или отклоняет изменение (клиент не синхронизирован).

Если изменение одобрено сервером, сервер затем уведомляет других клиентов (за исключением того, кто отправил изменение дельта) через GCM и отправляет многоадресное сообщение с тем же дельта-изменением. Клиенты обрабатывают это сообщение и обновляют свою базу данных.

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

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

Также проверьте этот учебник, чтобы начать работу с реализацией клиента CGM.

Ответ 4

это отвечает разработчикам, которые используют инфраструктуру Xamarin (см. fooobar.com/info/89616/...)

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

Реализация довольно проста:

1) создайте мобильное приложение на лазурном портале (вы можете попробовать его бесплатно здесь https://tryappservice.azure.com/)

2) подключите своего клиента к мобильному приложению. https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-xamarin-forms-get-started/

3) код для настройки локального репозитория:

const string path = "localrepository.db";

//Create our azure mobile app client
this.MobileService = new MobileServiceClient("the api address as setup on Mobile app services in azure");

//setup our local sqlite store and initialize a table
var repository = new MobileServiceSQLiteStore(path);

// initialize a Foo table
store.DefineTable<Foo>();

// init repository synchronisation
await this.MobileService.SyncContext.InitializeAsync(repository);
var fooTable = this.MobileService.GetSyncTable<Foo>();

4), затем нажмите и потяните ваши данные, чтобы убедиться, что у нас есть последние изменения:

await this.MobileService.SyncContext.PushAsync();
await this.saleItemsTable.PullAsync("allFoos", fooTable.CreateQuery());

https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-xamarin-forms-get-started-offline-data/