Synchronizacja baz danych klient-serwer

85

Szukam ogólnych strategii synchronizacji danych na serwerze centralnym z aplikacjami klienckimi, które nie zawsze są online.

W moim przypadku mam aplikację na telefon z systemem Android z bazą danych sqlite i aplikację internetową PHP z bazą danych MySQL.

Użytkownicy będą mogli dodawać i edytować informacje w aplikacji na telefon oraz w aplikacji internetowej. Muszę się upewnić, że zmiany wprowadzone w jednym miejscu są odzwierciedlane wszędzie, nawet jeśli telefon nie jest w stanie natychmiast skomunikować się z serwerem.

Nie przejmuję się tym, jak przenieść dane z telefonu na serwer lub odwrotnie. Wspominam o moich konkretnych technologiach tylko dlatego, że nie mogę na przykład korzystać z funkcji replikacji dostępnych w MySQL.

Wiem, że problem z synchronizacją danych klient-serwer istnieje od bardzo dawna i chciałbym uzyskać informacje - artykuły, książki, porady itp. - o wzorcach radzenia sobie z problemem. Chciałbym wiedzieć o ogólnych strategiach radzenia sobie z synchronizacją, aby porównać mocne i słabe strony oraz kompromisy.

Scott Saunders
źródło

Odpowiedzi:

96

Pierwszą rzeczą, którą musisz zdecydować, jest ogólna polityka dotycząca tego, która strona jest uważana za „autorytatywną” w przypadku sprzecznych zmian.

To znaczy: załóżmy, że rekord # 125 jest zmieniany na serwerze 5 stycznia o godzinie 22:00, a ten sam rekord jest zmieniany na jednym z telefonów (nazwijmy go Klientem A) 5 stycznia o godzinie 23:00. Ostatnia synchronizacja miała miejsce 3 stycznia. Następnie użytkownik łączy się ponownie, powiedzmy 8 stycznia.

Zidentyfikowanie tego, co należy zmienić, jest „łatwe” w tym sensie, że zarówno klient, jak i serwer znają datę ostatniej synchronizacji, więc wszystko, co zostało utworzone lub zaktualizowane (więcej informacji na ten temat poniżej) od ostatniej synchronizacji, musi zostać uzgodnione.

Załóżmy więc, że jedyny zmieniony rekord to # 125. Możesz zdecydować, że jedna z dwóch automatycznie „wygrywa” i nadpisuje drugą, albo musisz obsługiwać fazę uzgadniania, w której użytkownik może zdecydować, która wersja (serwer lub klient) jest właściwa, nadpisując drugą.

Ta decyzja jest niezwykle ważna i trzeba wyważyć „rolę” klientów. Zwłaszcza jeśli istnieje potencjalny konflikt nie tylko między klientem a serwerem, ale w przypadku, gdy różni klienci mogą zmienić te same rekordy.

[Zakładając, że numer 125 może zostać zmodyfikowany przez drugiego klienta (klienta B), istnieje szansa, że ​​klient B, który nie został jeszcze zsynchronizowany, dostarczy kolejną wersję tego samego rekordu, co spowoduje, że poprzednie rozwiązanie konfliktu będzie dyskusyjne]

Odnośnie do punktu „ utworzonego lub zaktualizowanego ” powyżej ... jak można prawidłowo zidentyfikować rekord, jeśli został on utworzony na jednym z klientów (zakładając, że ma to sens w domenie, w której występuje problem)? Załóżmy, że Twoja aplikacja zarządza listą kontaktów biznesowych. Jeśli Klient A mówi, że musisz dodać nowo utworzonego Johna Smitha, a serwer ma John Smith utworzony wczoraj przez Klienta D… czy tworzysz dwa rekordy, ponieważ nie możesz być pewien, że nie są to różne osoby? Czy poprosisz użytkownika również o pogodzenie tego konfliktu?

Czy klienci mają „własność” podzbioru danych? To znaczy, jeśli Klient B jest ustawiony jako „organ” w zakresie danych dla Obszaru 5, czy Klient A może modyfikować / tworzyć rekordy dla Obszaru 5, czy nie? (Ułatwiłoby to niektóre konflikty, ale może okazać się niewykonalne w twojej sytuacji).

Podsumowując, główne problemy to:

  • Jak zdefiniować „tożsamość”, biorąc pod uwagę, że odłączeni klienci mogli nie uzyskać dostępu do serwera przed utworzeniem nowego rekordu.
  • Poprzednia sytuacja, bez względu na to, jak skomplikowane jest rozwiązanie, może skutkować duplikacją danych, więc musisz przewidzieć, jak okresowo je rozwiązywać i jak informować klientów, że to, co uważali za „Rekord nr 675”, zostało faktycznie połączone z / zastąpione przez Rekord # 543
  • Zdecyduj, czy konflikty zostaną rozwiązane przez fiat (np. „Wersja serwera zawsze ma pierwszeństwo przed wersją klienta, jeśli poprzednia została zaktualizowana od czasu ostatniej synchronizacji”) lub przez interwencję ręczną
  • W przypadku fiat , zwłaszcza jeśli uznasz, że klient ma pierwszeństwo, musisz również zadbać o to, jak radzić sobie z innymi, jeszcze niezsynchronizowanymi klientami, którzy mogą mieć więcej zmian.
  • Poprzednie pozycje nie uwzględniają szczegółowości danych (w celu ułatwienia opisu). Wystarczy powiedzieć, że zamiast rozumowania na poziomie „rekordu”, jak w moim przykładzie, bardziej odpowiednie może być zapisanie zmian na poziomie pola. Lub pracować na zbiorze rekordów (np. Rekord osoby + rekord adresu + rekord kontaktów) w tym samym czasie, traktując ich agregat jako rodzaj „meta rekordu”.

Bibliografia:

(Ostatnie trzy pochodzą z biblioteki cyfrowej ACM, nie mam pojęcia, czy jesteś jej członkiem lub czy możesz je zdobyć za pośrednictwem innych kanałów).

Z witryny Dr.Dobbs :

  • Tworzenie aplikacji za pomocą SQL Server CE i SQL RDA - Bill Wagner 19 maja 2004 (Najlepsze praktyki projektowania aplikacji na komputer stacjonarny i komputer przenośny - Windows / .NET)

Z arxiv.org:

  • A Conflict-Free Replicated JSON Datatype - artykuł opisuje implementację JSON CRDT (bezkonfliktowe replikowane typy danych - CRDT - to rodzina struktur danych, które obsługują równoczesną modyfikację i gwarantują konwergencję takich równoległych aktualizacji).
p.marino
źródło
Dziękuję za Twoją odpowiedź. Jestem bardzo zainteresowany czytaniem o powszechnie stosowanych / możliwych rozwiązaniach (wady, zalety, porównania) zarysowanych przez Ciebie problemów.
Scott Saunders
Przypuszczam, że sprawdziłeś już Wikipedię i rzeczy, do których prowadzą linki, prawda?
p.marino
3
+1 To świetny post z bardzo ważnymi informacjami na ten temat. Brakujący punkt: synchronizacja usuniętych rekordów.
Stefan Steinegger,
7
Zwykle uważam „usunięte” za specjalny przypadek „zaktualizowane”, zwłaszcza że w tego rodzaju sytuacjach preferuję „usuwanie logiczne” zamiast „usuwanie fizyczne”. Tak więc dla mnie „usunięte” po stronie master lub slave oznacza, że ​​„specjalna flaga logiczna jest usunięta została odwrócona” bardziej niż cokolwiek innego.
p.marino
Dzięki. Dodałem jeszcze jeden link do innego artykułu (dr.dobbs) i zaktualizuję bibliografię, jeśli znajdę coś innego.
p.marino
9

Zalecałbym, aby w każdej tabeli znajdowała się kolumna sygnatury czasowej i za każdym razem, gdy wstawiasz lub aktualizujesz, aktualizuj wartość sygnatury czasowej każdego wiersza, którego dotyczy problem. Następnie iterujesz po wszystkich tabelach, sprawdzając, czy sygnatura czasowa jest nowsza niż ta, którą masz w docelowej bazie danych. Jeśli jest nowszy, sprawdź, czy musisz go wstawić lub zaktualizować.

Uwaga 1: pamiętaj o fizycznym usuwaniu, ponieważ wiersze są usuwane ze źródłowej bazy danych i musisz zrobić to samo w bazie danych serwera. Możesz rozwiązać ten problem, unikając fizycznego usuwania lub rejestrowania każdego usunięcia w tabeli z sygnaturami czasowymi. Coś takiego: DeletedRows = (id, table_name, pk_column, pk_column_value, timestamp)Więc musisz przeczytać wszystkie nowe wiersze tabeli DeletedRows i wykonać usuwanie na serwerze używając nazwa_tabeli, pk_column i pk_column_value.

Uwaga 2: bądź świadomy FK, ponieważ wstawianie danych do tabeli, która jest powiązana z inną tabelą, może się nie powieść. Przed synchronizacją danych należy dezaktywować każdy FK.

Francisco Goldenstein
źródło
3
zegary muszą być zsynchronizowane
tofutim
6

Jeśli ktoś ma do czynienia z podobnym problemem projektowym i musi zsynchronizować zmiany na wielu urządzeniach z Androidem, polecam sprawdzenie Google Cloud Messaging dla Androida (GCM).

Pracuję nad jednym rozwiązaniem, w którym zmiany wprowadzone na jednym kliencie muszą być propagowane do innych klientów. Właśnie zaimplementowałem dowód implementacji koncepcji (serwer i klient) i działa to jak marzenie.

Zasadniczo każdy klient wysyła zmiany delta do serwera. Np. Identyfikator zasobu ABCD1234 zmienił się z wartości 100 na 99.

Serwer sprawdza poprawność tych zmian w swojej bazie danych i albo zatwierdza zmianę (klient jest zsynchronizowany) i aktualizuje swoją bazę danych, albo odrzuca zmianę (klient nie jest zsynchronizowany).

Jeśli zmiana zostanie zatwierdzona przez serwer, serwer powiadamia następnie innych klientów (z wyjątkiem tego, który wysłał zmianę delta) za pośrednictwem GCM i wysyła wiadomość multiemisji zawierającą tę samą zmianę delta. Klienci przetwarzają tę wiadomość i aktualizują swoją bazę danych.

Fajne jest to, że te zmiany rozprzestrzeniają się niemal natychmiastowo !!! jeśli te urządzenia są w trybie online. I nie muszę implementować żadnego mechanizmu odpytywania na tych klientach.

Pamiętaj, że jeśli urządzenie jest zbyt długo w trybie offline, a w kolejce GCM czeka na dostarczenie ponad 100 wiadomości, GCM odrzuci te wiadomości i wyśle ​​specjalną wiadomość, gdy urządzenie wróci do trybu online. W takim przypadku klient musi wykonać pełną synchronizację z serwerem.

Zapoznaj się również z tym samouczkiem, aby rozpocząć wdrażanie klienta CGM.

jogo
źródło
5

to odpowiada deweloperom korzystającym z platformy Xamarin (zobacz /programming/40156342/sync-online-offline-data )

Bardzo prostym sposobem osiągnięcia tego za pomocą platformy Xamarin jest użycie synchronizacji danych w trybie offline platformy Azure, ponieważ umożliwia ona wypychanie i pobieranie danych z serwera na żądanie. Operacje odczytu są wykonywane lokalnie, a operacje zapisu są wypychane na żądanie; W przypadku zerwania połączenia sieciowego operacje zapisu są umieszczane w kolejce do momentu przywrócenia połączenia, a następnie wykonywane.

Wdrożenie jest dość proste:

1) Stwórz aplikację mobilną w portalu Azure (możesz wypróbować ją bezpłatnie tutaj https://tryappservice.azure.com/ )

2) połącz swojego klienta z aplikacją mobilną. https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-xamarin-forms-get-started/

3) kod do konfiguracji lokalnego repozytorium:

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) następnie wypchnąć i pobrać dane, aby upewnić się, że mamy najnowsze zmiany:

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/

Ben Ishiyama-Levy
źródło
0

Proponuję również przyjrzeć się Symmetricds . jest to biblioteka replikacji SQLite dostępna dla systemów Android. możesz go użyć do zsynchronizowania bazy danych klienta i serwera, proponuję również mieć osobne bazy danych na serwerze dla każdego klienta. Próba przechowywania danych wszystkich użytkowników w jednej bazie danych mysql nie zawsze jest najlepszym pomysłem. Szczególnie jeśli dane użytkowników będą szybko rosły.

Hossein Shahdoost
źródło
0

Nazwijmy to CUDR Sync problemem (nie lubię CRUD - ponieważ Create / Update / Delete to zapisy i powinny być sparowane razem)

Problem może być również rozpatrywany w trybie odpisywania w trybie offline lub najpierw zapisu online . Podejście do zapisu w trybie offline ma problem z konfliktem unikalnych identyfikatorów, a także z wieloma wywołaniami sieciowymi dla tej samej transakcji, co zwiększa ryzyko (lub koszty) ...

Osobiście uważam, że podejście polegające na pisaniu online jest łatwiejsze w zarządzaniu (więc będzie to jedyne źródło prawdy - skąd wszystko inne jest synchronizowane). Podejście do zapisu online będzie wymagało, aby użytkownicy nie mogli najpierw pisać offline - będą pisać w trybie offline, otrzymując poprawną odpowiedź z formularza online.

Może najpierw czytać offline, a gdy tylko sieć będzie dostępna, pobrać dane z online i zaktualizować lokalną bazę danych, a następnie zaktualizować interfejs użytkownika ...

Jednym ze sposobów uniknięcia konfliktu unikatowych identyfikatorów byłoby użycie kombinacji unikalnego identyfikatora użytkownika + nazwy tabeli lub identyfikatora tabeli + identyfikatora wiersza (wygenerowanego przez sqlite) ... a następnie użycie z nią zsynchronizowanej kolumny flagi logicznej ... ale nadal rejestrację należy najpierw przeprowadzić online, aby uzyskać unikalny identyfikator, na którym zostaną wygenerowane wszystkie inne identyfikatory ... tutaj problem będzie również występował, jeśli zegary nie są zsynchronizowane - o czym ktoś wspomniany powyżej ...

Smoczy ogień
źródło
Ponadto podejście do zapisu offline będzie miało problem z odinstalowaniem aplikacji, wszystkie dane, które nie zostały przesłane online, zostaną usunięte
DragonFire