Шаблон / алгоритм синхронизации клиент-сервер?

StackOverflow https://stackoverflow.com/questions/413086

Вопрос

У меня такое чувство, что где-то там должны быть шаблоны синхронизации клиент-сервер.Но мне совершенно не удалось найти ни одного из них в Google.

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

Существуют ли какие-либо шаблоны / рекомендуемые практики для такой ситуации, или если вы ни о каких не знаете - каков был бы ваш подход?

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

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

Есть какие-нибудь мысли?

Это было полезно?

Решение

Вам следует посмотреть, как работает распределенное управление изменениями.Посмотрите на SVN, CVS и другие репозитории, которые управляют работой deltas.

У вас есть несколько вариантов использования.

  • Синхронизируйте изменения.Ваш подход с журналом изменений (или дельта-историей) хорошо подходит для этого.Клиенты отправляют свои дельты на сервер;сервер консолидирует и распределяет дельты между клиентами.Это типичный случай.Базы данных называют это "репликацией транзакций".

  • Клиент потерял синхронизацию.Либо из-за резервного копирования / восстановления, либо из-за ошибки.В этом случае клиенту необходимо получить текущее состояние от сервера, не проходя через дельты.Это копия от мастера до мелочей, к черту дельты и производительность.Это одноразовая вещь;клиент сломан;не пытайтесь оптимизировать это, просто создайте надежную копию.

  • Клиент подозрителен.В этом случае вам необходимо сравнить клиент с сервером, чтобы определить, является ли клиент актуальным и нуждается ли в каких-либо дельтах.

Вы должны следовать шаблону проектирования базы данных (и SVN), последовательно нумеруя каждое изменение.Таким образом, клиент может сделать тривиальный запрос ("Какую ревизию я должен иметь?"), прежде чем пытаться синхронизировать.И даже тогда запрос ("Все дельты с 2149 года") восхитительно прост для обработки клиентом и сервером.

Другие советы

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

Синхронизация данных - довольно широкое понятие, и здесь слишком много вопросов для обсуждения.Она охватывает целый ряд различных подходов с их плюсами и минусами.Вот одна из возможных классификаций, основанная на двух перспективах:Синхронный / Асинхронный, Клиент/Сервер / Одноранговый.Реализация синхронизации сильно зависит от этих факторов, сложности модели данных, объема передаваемых и хранимых данных и других требований.Таким образом, в каждом конкретном случае выбор должен быть в пользу простейшей реализации, соответствующей требованиям приложения.

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

  • Синхронизация всего документа или базы данных используется в облачных приложениях, таких как Dropbox, Google Drive или Яндекс.Диск.Когда пользователь редактирует и сохраняет файл, новая версия файла полностью загружается в облако, перезаписывая предыдущую копию.В случае конфликта обе версии файла сохраняются, чтобы пользователь мог выбрать, какая версия является более актуальной.
  • Синхронизация пар ключ-значение может использоваться в приложениях с простой структурой данных, где переменные считаются атомарными, т.е.не разделен на логические составляющие.Эта опция аналогична синхронизации целых документов, поскольку и значение, и документ могут быть полностью перезаписаны.Однако с точки зрения пользователя документ представляет собой сложный объект, состоящий из множества частей, а пара ключ-значение - это всего лишь короткая строка или число.Следовательно, в данном случае мы можем использовать более простую стратегию разрешения конфликта, считая значение более релевантным, если оно менялось последним.
  • Синхронизация данных, структурированных в виде дерева или графика, используется в более сложных приложениях, где объем данных достаточно велик, чтобы отправлять базу данных целиком при каждом обновлении.В этом случае конфликты должны разрешаться на уровне отдельных объектов, полей или отношений.Мы в первую очередь сосредоточены на этом варианте.

Итак, мы собрали наши знания в этой статье, которая, я думаю, может быть очень полезна всем, кто интересуется этой темой => Синхронизация данных в приложениях iOS на основе Core Data (http://blog.denivip.ru/index.php/2014/04/data-syncing-in-core-data-based-ios-apps/?lang=en)

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

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

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

Я создал систему, подобную этой, для приложения около 8 лет назад, и я могу рассказать о нескольких путях ее развития по мере роста использования приложения.

Я начал с регистрации каждого изменения (вставки, обновления или удаления) с любого устройства в «истории». Таблица. Так что, если, например, кто-то изменит свой номер телефона в разделе «контакт» таблица, система отредактирует поле contact.phone, а также добавит запись истории с действием = обновление, поле = телефон, запись = [идентификатор контакта], значение = [новый номер телефона]. Затем всякий раз, когда устройство выполняет синхронизацию, оно загружает элементы истории с момента последней синхронизации и применяет их к своей локальной базе данных. Это звучит как «репликация транзакций» шаблон, описанный выше.

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

В конце концов я узнал, что UUIDs могут этого избежать, но к тому времени моя база данных становилась довольно большой, и я боялся, что полная реализация UUID создаст проблему производительности. Поэтому вместо использования полных UUID я начал использовать случайно сгенерированные 8-символьные буквенно-цифровые ключи в качестве идентификаторов, и я оставил свой существующий код на месте для обработки конфликтов. Где-то между моими нынешними 8-символьными клавишами и 36 символами UUID должно быть хорошее место, которое устраняло бы конфликты без ненужного раздувания, но, поскольку у меня уже есть код разрешения конфликтов, экспериментировать с этим не было приоритетом. .

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top