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


224

У мене таке відчуття, що там повинні бути схеми синхронізації клієнт-сервер. Але я цілком не зміг підняти Google.

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

Чи є якісь зразки / хороші практики для такої ситуації, або якщо ви нічого не знаєте - яким би був ваш підхід?

Нижче описано, як я зараз думаю вирішити це питання: Паралельно з даними, журнал модифікацій буде проводитись із зазначенням усіх транзакцій. Коли клієнт підключається, він отримує всі зміни після останньої перевірки, у консолідованому вигляді (сервер проходить списки та видаляє доповнення, за якими слідують вилучення, об'єднує оновлення для кожного атома тощо). Et voila, ми в курсі.

Альтернативою може бути збереження дати модифікації для кожного запису, а замість того, щоб виконувати видалення даних, просто позначте їх як видалені.

Будь-які думки?


27
погодився, що дуже мало розмов про шаблони подібних речей ... хоча цей сценарій є досить поширеним
Джек Укледжа

Відповіді:


88

Ви повинні подивитися, як працює розподілений менеджмент змін. Подивіться на SVN, CVS та інші сховища, які управляють дельтами.

У вас є кілька випадків використання.

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

  • Клієнт втратив синхронізацію. Або через резервну копію / відновлення або через помилку. У цьому випадку клієнту потрібно отримати поточний стан з сервера, не проходячи дельти. Це копія від майстра до деталей, дельта і виконання будуть прокляті. Це разова річ; клієнт зламаний; не намагайтеся оптимізувати це, просто реалізуйте надійну копію.

  • Клієнт підозрілий. У цьому випадку вам потрібно порівняти клієнта з сервером, щоб визначити, чи клієнт оновлений та чи потрібні дельтати.

Вам слід дотримуватися схеми дизайну бази даних (та SVN), щоб послідовно нумерувати кожну зміну. Таким чином клієнт може зробити тривіальний запит ("Яку редакцію я повинен мати?"), Перш ніж спробувати синхронізуватись. І навіть тоді запит ("Усі дельти з 2149 року") дивовижно простий для клієнта та сервера для обробки.


Ви можете пояснити, що саме таке дельта? Моя здогадка, це комбінація хеш / часових позначок ... Я хотів би почути це від пана.
Аніс

Дельта посилається на зміну між двома редакціями. Наприклад, якщо ім’я користувача змінилося, дельта може бути на зразок {revision: 123, name: "John Doe"}
dipole_moment

31

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

Синхронізація даних - це досить широке поняття, і є занадто багато для обговорення. Він охоплює цілий спектр різних підходів із їх перевагами та недоліками. Ось одна з можливих класифікацій, заснована на двох перспективах: Синхронна / Асинхронна, Клієнтська / Серверна / Peer-to-Peer. Реалізація синхронізації сильно залежить від цих факторів, складності моделі даних, кількості переданих та зберігаються даних та інших вимог. Тож у кожному конкретному випадку вибір повинен підтримувати найпростішу реалізацію, яка відповідає вимогам програми.

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

  • Синхронізація цілого документа або бази даних використовується в хмарних додатках, таких як Dropbox, Google Drive або Yandex.Disk. Коли користувач редагує та зберігає файл, нова версія файлу завантажується повністю у хмару, замінюючи попередню копію. У разі конфлікту обидві версії файлів зберігаються, щоб користувач міг вибрати, яка версія є більш актуальною.
  • Синхронізація пар ключ-значення може використовуватися в додатках із простою структурою даних, де змінні вважаються атомними, тобто не поділяються на логічні компоненти. Цей параметр схожий на синхронізацію цілих документів, оскільки і значення, і документ можуть бути перезаписані повністю. Однак з точки зору користувача документ - це складний об'єкт, що складається з багатьох частин, але пара ключ-значення - це лише коротка рядок або число. Тому в цьому випадку ми можемо використовувати більш просту стратегію вирішення конфлікту, вважаючи значення більш релевантним, якщо воно останнє змінилося.
  • Синхронізація даних, структурованих у вигляді дерева або графіка, використовується в більш досконалих додатках, де обсяг даних є достатньо великим, щоб надсилати базу даних у повному обсязі при кожному оновленні. У цьому випадку конфлікти доводиться вирішувати на рівні окремих об'єктів, полів або відносин. Ми в першу чергу зосереджені на цьому варіанті.

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


3
^^^^^^ це, безумовно, найкраща відповідь, хлопці!
hgoebl

Я погоджуюся, Денис багато вніс у тему + посилання на статті - приголомшливі. Також розповідає про ЗЗ, згаданий DanielPaull. Відповідь С.Лотта хороша, але це набагато глибше.
Кристян

28

Те, що вам справді потрібно, - це оперативна трансформація (ОТ). Це навіть може задовольнити конфлікти у багатьох випадках.

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


7
Даніель, вказівник на відповідні ресурси буде вдячний.
Паранд

4
Я просто перечитав статтю у Вікіпедії. Він пройшов довгий шлях і має безліч відповідних посилань внизу цієї сторінки. Я б вказав на твір Ченчженга Сонця - його творчість посилається на wikipedia. en.wikipedia.org/wiki/Operational_transformation . Сподіваюся, що це допомагає!
Даніель Полл

13

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


2
Послідовні номери тут ваш друг. Подумайте про стійкі черги повідомлень.
Даніель Полл

7

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

Я почав із реєстрації кожної зміни (вставлення, оновлення чи видалення) з будь-якого пристрою в таблицю "історії". Так, якщо, наприклад, хтось змінить свій номер телефону в таблиці "контакт", система редагує поле contact.phone, а також додасть запис історії з діями = оновлення, поле = телефон, запис = [контакт ID], value = [новий номер телефону]. Потім кожен раз, коли пристрій синхронізується, він завантажує елементи історії з часу останньої синхронізації та застосовує їх до своєї локальної бази даних. Це звучить як описаний вище шаблон "реплікації транзакції".

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

Врешті-решт я дізнався, що UUID можуть цього уникнути, але до того моєї бази даних стало досить багато, і я побоювався, що повне впровадження UUID створить проблему з ефективністю. Тому замість використання повних UUID я почав використовувати випадково згенеровані 8-символьні буквено-цифрові ключі як ідентифікатори, і я залишив свій існуючий код на місці для вирішення конфліктів. Десь між моїми поточними 8-символьними клавішами та 36 символами UUID повинно бути приємне місце, яке б усувало конфлікти без зайвого нахилу, але оскільки у мене вже є код вирішення конфлікту, експеримент із цим не було пріоритетним завданням .

Наступною проблемою було те, що таблиця історії була приблизно в 10 разів більша, ніж уся решта бази даних. Це робить зберігання дорогим, і будь-яке обслуговування в таблиці історії може бути болючим. Зберігання цілої таблиці дозволяє користувачам відмовляти будь-які попередні зміни, але це почало відчувати себе надмірними. Тому я додав процедуру до процесу синхронізації, коли, якщо елемент історії, який останній завантажений пристрій більше не існує в таблиці історії, сервер не дає йому останніх елементів історії, а натомість надає йому файл, що містить усі дані для цей рахунок. Потім я додав cronjob, щоб видалити предмети історії, старші 90 днів. Це означає, що користувачі все ще можуть відмовляти зміни, які не досягли 90 днів, і якщо вони синхронізуються щонайменше раз на 90 днів, оновлення будуть поступовими, як і раніше. Але якщо вони чекають довше 90 днів,

Ця зміна зменшила розмір таблиці історії майже на 90%, тому тепер підтримка таблиці історії робить базу даних лише вдвічі більшою, а не в десять разів більшою. Ще одна перевага цієї системи полягає в тому, що синхронізація все ще може працювати без таблиці історії, якщо це потрібно - наприклад, якщо мені потрібно було виконати деяке обслуговування, яке тимчасово відключило її. Або я можу запропонувати різні періоди відкату рахунків за різними ціновими точками. Якщо для завантаження є більше 90 днів змін, повний файл, як правило, є більш ефективним, ніж інкрементальний формат.

Якби я починав сьогодні, я би пропустив перевірку конфліктів ідентифікаторів і просто націлювався на ключову довжину, достатню для усунення конфліктів, з певною перевіркою помилок на всякий випадок. Але таблиця історії та комбінація поступових завантажень для останніх оновлень або повного завантаження при необхідності працює добре.


1

Для синхронізації дельти (зміни) ви можете використовувати шаблон pubsub, щоб опублікувати зміни для всіх підписаних клієнтів, такі послуги, як штовхач, можуть це зробити.

Для дзеркала баз даних деякі веб-рамки використовують локальну міні-базу даних для синхронізації бази даних сервера з локальною у базі даних браузера, підтримується часткова синхронізація. Перевірте метерор .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.