it-swarm.com.de

Client-Server-Synchronisationsmuster/-algorithmus?

Ich habe das Gefühl, dass es da draußen Client-Server-Synchronisationsmuster geben muss. Aber ich habe es absolut nicht geschafft, einen zu googeln.

Die Situation ist ziemlich einfach - Server ist der zentrale Knoten, an dem sich mehrere Clients mit denselben Daten verbinden und diese bearbeiten. Daten können in Atome aufgeteilt werden, im Konfliktfall hat das, was sich auf dem Server befindet, Priorität (um zu vermeiden, dass der Benutzer in Konfliktlösung gerät). Partielle Synchronisation wird aufgrund potenziell großer Datenmengen bevorzugt.

Gibt es Muster/gute Praktiken für eine solche Situation oder wenn Sie keine wissen - wie würden Sie vorgehen?

Im Folgenden denke ich, es zu lösen: Parallel zu den Daten wird ein Änderungsjournal gehalten, bei dem alle Transaktionen mit einem Zeitstempel versehen sind. Wenn der Client eine Verbindung herstellt, erhält er alle Änderungen seit der letzten Prüfung in konsolidierter Form (der Server führt Listen durch und entfernt Zusätze, die gelöscht werden, führt Aktualisierungen für jedes Atom ein, usw ..) ..__ auf dem neusten Stand.

Alternativ können Sie das Änderungsdatum für jeden Datensatz beibehalten. Statt Daten zu löschen, markieren Sie sie einfach als gelöscht.

Irgendwelche Gedanken?

206
tm_lv

Sie sollten prüfen, wie verteiltes Änderungsmanagement funktioniert. Schauen Sie sich SVN, CVS und andere Repositories an, die die Arbeit von Deltas verwalten.

Sie haben mehrere Anwendungsfälle.

  • Änderungen synchronisieren Ihr Änderungsprotokoll (oder Delta-Verlauf) sieht dafür gut aus. Clients senden ihre Deltas an den Server; Server konsolidiert und verteilt die Deltas an die Clients. Dies ist der typische Fall. Datenbanken nennen dies "Transaktionsreplikation".

  • Client hat die Synchronisation verloren. Entweder durch ein Backup/Restore oder wegen eines Fehlers. In diesem Fall muss der Client den aktuellen Status vom Server abrufen, ohne die Deltas zu durchlaufen. Dies ist eine Kopie vom Meister bis ins Detail, Deltas und Performance sind verdammt. Es ist eine einmalige Sache. der Kunde ist kaputt; Versuchen Sie nicht, dies zu optimieren, sondern implementieren Sie einfach eine zuverlässige Kopie.

  • Der Kunde ist verdächtig. In diesem Fall müssen Sie den Client mit dem Server vergleichen, um festzustellen, ob der Client auf dem neuesten Stand ist und Deltas benötigt.

Sie sollten dem Datenbank- (und SVN-) Entwurfsmuster folgen, bei dem Sie jede Änderung fortlaufend nummerieren. Auf diese Weise kann ein Client eine triviale Anforderung stellen ("Welche Revision sollte ich haben?"), Bevor er eine Synchronisierung versucht. Und selbst dann ist die Abfrage ("Alle Deltas seit 2149") für Client und Server einfach zu verarbeiten.

82
S.Lott

Als Teil des Teams habe ich eine ganze Reihe von Projekten durchgeführt, bei denen Daten synchronisiert wurden. Daher sollte ich kompetent sein, diese Frage zu beantworten.

Die Datensynchronisierung ist ein ziemlich umfassendes Konzept, und es gibt viel zu viel zu diskutieren. Es werden verschiedene Ansätze mit ihren Vor- und Nachteilen behandelt. Hier ist eine der möglichen Klassifizierungen basierend auf zwei Perspektiven: Synchron/Asynchron, Client/Server/Peer-to-Peer. Die Implementierung der Synchronisierung hängt stark von diesen Faktoren, der Komplexität des Datenmodells, der übertragenen und gespeicherten Datenmenge und anderen Anforderungen ab. Daher sollte in jedem Einzelfall die Wahl auf die einfachste Implementierung fallen, die den App-Anforderungen entspricht.

Basierend auf einer Überprüfung der vorhandenen Standardlösungen können wir verschiedene Hauptklassen der Synchronisierung definieren, die sich in der Granularität der zu synchronisierenden Objekte unterscheiden:

  • Die Synchronisierung eines gesamten Dokuments oder einer Datenbank wird in cloudbasierten Anwendungen wie Dropbox, Google Drive oder Yandex.Disk verwendet. Wenn der Benutzer eine Datei bearbeitet und speichert, wird die neue Dateiversion vollständig in die Cloud hochgeladen und die frühere Kopie überschrieben. Im Konfliktfall werden beide Dateiversionen gespeichert, damit der Benutzer auswählen kann, welche Version relevanter ist.
  • Das Synchronisieren von Schlüssel-Wert-Paaren kann in Apps mit einer einfachen Datenstruktur verwendet werden, bei denen die Variablen als atomar betrachtet werden, d. H. Nicht in logische Komponenten unterteilt. Diese Option ähnelt dem Synchronisieren ganzer Dokumente, da sowohl der Wert als auch das Dokument vollständig überschrieben werden können. Aus Benutzersicht ist ein Dokument jedoch ein komplexes Objekt, das aus vielen Teilen besteht. Ein Schlüssel-Wert-Paar ist jedoch nur eine kurze Zeichenfolge oder eine Zahl. Daher können wir in diesem Fall eine einfachere Strategie der Konfliktlösung anwenden, wobei der Wert relevanter ist, wenn er zuletzt geändert wurde.
  • Die Synchronisierung von Daten, die als Baum oder Diagramm strukturiert sind, wird in anspruchsvolleren Anwendungen verwendet, bei denen die Datenmenge groß genug ist, um die Datenbank bei jeder Aktualisierung vollständig zu senden. In diesem Fall müssen Konflikte auf der Ebene einzelner Objekte, Felder oder Beziehungen gelöst werden. Wir konzentrieren uns hauptsächlich auf diese Option.

Wir haben unser Wissen in diesem Artikel zusammengetragen, der meiner Meinung nach für alle, die sich für das Thema interessieren, sehr nützlich sein könnte => Datensynchronisierung in datenbasierten iOS - Kernanwendungen ( http://blog.denivip.ru/index.php/2014/04/Datensynchronisierung in datenbasierten Core-iOS-Apps /? Lang = de )

28

Was Sie wirklich brauchen, ist Operational Transform (OT). Dies kann in vielen Fällen sogar für Konflikte sorgen.

Dies ist immer noch ein aktives Forschungsgebiet, aber es gibt Implementierungen verschiedener OT -Algorithmen. Ich bin bereits seit einigen Jahren in solche Forschungen involviert, also lassen Sie mich wissen, ob diese Route Sie interessiert, und ich werde Sie gerne mit relevanten Ressourcen versorgen.

23
Daniel Paull

Die Frage ist nicht ganz klar, aber ich würde in optimistisches Sperren schauen, wenn ich Sie wäre .. Sie kann mit einer Sequenznummer implementiert werden, die der Server für jeden Datensatz zurückgibt. Wenn ein Client versucht, den Datensatz zurückzuspeichern, wird die vom Server erhaltene Sequenznummer angegeben. Wenn die Folgenummer mit dem übereinstimmt, was sich zum Zeitpunkt des Updates in der Datenbank befindet, ist die Aktualisierung zulässig und die Folgenummer wird erhöht. Wenn die Folgenummern nicht übereinstimmen, ist die Aktualisierung nicht zulässig.

11
erikkallen

Ich habe vor etwa 8 Jahren ein solches System für eine App entwickelt, und ich kann ein paar Möglichkeiten mitteilen, wie es sich entwickelt hat, da die App-Nutzung gewachsen ist.

Ich begann damit, jede Änderung (Einfügen, Aktualisieren oder Löschen) von jedem Gerät in einer "Protokoll" -Tabelle zu protokollieren. Wenn also zum Beispiel jemand seine Telefonnummer in der "Kontakt" -Tabelle ändert, bearbeitet das System das Feld contact.phone und fügt auch einen Verlaufsdatensatz mit action = update, field = phone, record = [contact ID], hinzu. Wert = [neue Telefonnummer]. Immer wenn ein Gerät synchronisiert wird, lädt es die Verlaufselemente seit der letzten Synchronisierung herunter und wendet sie auf die lokale Datenbank an. Das klingt wie das oben beschriebene "Transaktionsreplikationsmuster".

Ein Problem besteht darin, IDs eindeutig zu halten, wenn Elemente auf verschiedenen Geräten erstellt werden könnten. UUIDs wusste ich zu Beginn nicht, daher habe ich automatisch inkrementierende IDs verwendet und auf dem zentralen Server laufenden Code geschrieben, um die von Geräten hochgeladenen neuen IDs zu überprüfen und bei Konflikten in eine eindeutige ID umzuwandeln das Quellgerät anweisen, die ID in seiner lokalen Datenbank zu ändern. Das Ändern der IDs von neuen Datensätzen war nicht so schlecht, aber wenn ich zum Beispiel ein neues Element in der Kontakttabelle und dann ein neues zugehöriges Element in der Ereignistabelle erstellt habe, habe ich nun auch Fremdschlüssel, die ich auch benötigen überprüfen und aktualisieren.

Schließlich lernte ich, dass UUIDs dies vermeiden können, aber bis dahin wurde meine Datenbank ziemlich groß und ich befürchtete, eine vollständige UUID-Implementierung würde zu einem Leistungsproblem führen. Anstatt vollständige UUIDs zu verwenden, verwendete ich zufällig generierte alphanumerische 8-stellige Schlüssel als IDs und beließ meinen vorhandenen Code, um Konflikte zu behandeln. Irgendwo zwischen meinen aktuellen 8-stelligen Tasten und den 36 Zeichen einer UUID muss es einen Sweet-Spot geben, der Konflikte ohne unnötige Aufblähung beseitigt. Da ich jedoch bereits einen Konfliktlösungscode habe, habe ich nicht versucht, damit zu experimentieren .

Das nächste Problem war, dass die Verlaufstabelle etwa zehnmal größer war als der Rest der Datenbank. Dies macht den Speicher teuer und jede Wartung der Verlaufstabelle kann schmerzhaft sein. Durch Beibehalten dieser gesamten Tabelle können Benutzer alle vorherigen Änderungen rückgängig machen, was sich jedoch als übertrieben anfühlte. Daher fügte ich dem Synchronisierungsprozess eine Routine hinzu, in der der Server nicht die letzten Verlaufselemente erhält, wenn das Protokollelement, das ein Gerät zuletzt heruntergeladen hat, nicht mehr in der Protokolltabelle vorhanden ist, sondern eine Datei mit allen Daten dieser Account. Dann habe ich einen Cronjob hinzugefügt, um Verlaufselemente zu löschen, die älter als 90 Tage sind. Dies bedeutet, dass Benutzer Änderungen, die weniger als 90 Tage alt sind, weiterhin rückgängig machen können. Wenn sie alle 90 Tage mindestens einmal synchronisiert werden, werden die Updates wie zuvor inkrementell durchgeführt. Wenn sie jedoch länger als 90 Tage warten, ersetzt die App die gesamte Datenbank.

Durch diese Änderung wurde die Größe der Protokolltabelle um fast 90% reduziert, sodass die Datenbank jetzt nur noch doppelt so groß ist, anstatt sie zehnmal so groß zu machen. Ein weiterer Vorteil dieses Systems besteht darin, dass die Synchronisierung bei Bedarf auch ohne die Verlaufstabelle funktionieren könnte - wenn ich beispielsweise eine Wartung durchführen müsste, die das System vorübergehend offline stellte. Oder ich könnte verschiedene Rollback-Zeiträume für Konten mit unterschiedlichen Preispunkten anbieten. Wenn der Download mehr als 90 Tage dauert, ist die gesamte Datei normalerweise effizienter als das inkrementelle Format.

Wenn ich heute noch einmal von vorne anfangen würde, würde ich die ID-Konfliktprüfung überspringen und nur eine Schlüssellänge anstreben, die ausreicht, um Konflikte zu beseitigen, und zwar mit einer Art Fehlerprüfung für alle Fälle. Die Verlaufstabelle und die Kombination von inkrementellen Downloads für die letzten Aktualisierungen oder eines vollständigen Downloads bei Bedarf haben jedoch gut funktioniert.

0
arlomedia

Bei der Delta-Synchronisierung (Änderung) können Sie das Pubsub-Muster verwenden, um Änderungen wieder allen abonnierten Clients zu veröffentlichen. Services wie pusher können dies tun.

Bei der Datenbankspiegelung verwenden einige Web-Frameworks eine lokale Mini-Datenbank, um die serverseitige Datenbank mit der lokalen Datenbank in der Browser-Datenbank zu synchronisieren. Die teilweise Synchronisierung wird unterstützt. Überprüfen Sie Meteror .

0
fuyi