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

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

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

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

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

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

Есть мысли?

Ответы [ 6 ]

82 голосов
/ 05 января 2009

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

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

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

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

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

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

25 голосов
/ 16 марта 2016

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

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

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

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

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

23 голосов
/ 05 января 2009

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

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

12 голосов
/ 05 января 2009

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

3 голосов
/ 23 января 2019

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

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

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

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

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

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

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

0 голосов
/ 17 мая 2015

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...