Schema / algoritmo di sincronizzazione client-server?


224

Ho la sensazione che ci debbano essere schemi di sincronizzazione client-server. Ma non sono riuscito completamente a cercarne uno.

La situazione è piuttosto semplice: il server è il nodo centrale a cui più client si connettono e manipolano gli stessi dati. I dati possono essere suddivisi in atomi, in caso di conflitto, qualunque cosa sia sul server, ha la priorità (per evitare che l'utente risolva i conflitti). La sincronizzazione parziale è preferita a causa di grandi quantità di dati.

Ci sono schemi / buone pratiche per tale situazione, o se non ne conosci nessuno - quale sarebbe il tuo approccio?

Di seguito è come ora penso di risolverlo: parallelamente ai dati, verrà tenuto un diario delle modifiche, con tutte le transazioni timestamp. Quando il client si connette, riceve tutte le modifiche dall'ultimo controllo, in forma consolidata (il server passa attraverso gli elenchi e rimuove le aggiunte seguite dalle eliminazioni, unisce gli aggiornamenti per ciascun atomo, ecc.). Et voilà, siamo aggiornati.

Alternativa sarebbe mantenere la data di modifica per ogni record e invece di eseguire l'eliminazione dei dati, contrassegnarli come eliminati.

qualche idea?


27
concordato che si parla molto poco di schemi per questo genere di cose ... anche se questo scenario è abbastanza comune
Jack Ukleja,

Risposte:


88

Dovresti vedere come funziona la gestione distribuita delle modifiche. Guarda SVN, CVS e altri repository che gestiscono il lavoro dei delta.

Hai diversi casi d'uso.

  • Sincronizza le modifiche. Il tuo approccio al registro delle modifiche (o cronologia delta) è perfetto per questo. I client inviano i loro delta al server; il server consolida e distribuisce i delta ai client. Questo è il caso tipico. I database chiamano questa "replica delle transazioni".

  • Il client ha perso la sincronizzazione. Attraverso un backup / ripristino o a causa di un bug. In questo caso, il client deve ottenere lo stato corrente dal server senza passare attraverso i delta. Questa è una copia dal master ai dettagli, i delta e le performance sono dannati. È una cosa una tantum; il cliente è rotto; non tentare di ottimizzare questo, basta implementare una copia affidabile.

  • Il cliente è sospettoso. In questo caso, è necessario confrontare il client con il server per determinare se il client è aggiornato e necessita di delta.

È necessario seguire il modello di progettazione del database (e SVN) di numerazione sequenziale di ogni modifica. In questo modo un client può fare una richiesta banale ("Quale revisione devo avere?") Prima di tentare di sincronizzare. E anche allora, la query ("Tutti i delta dal 2149") è deliziosamente semplice da elaborare per client e server.


Potresti spiegare che cos'è esattamente un delta? La mia ipotesi sarebbe che è una combinazione di hash / timestamp ... Mi piacerebbe sentirti.
Anis,

Un delta si riferisce al cambiamento tra due revisioni. Ad esempio, se il nome di un utente è cambiato, il delta può essere simile a {revisione: 123, nome: "John Doe"}
dipole_moment

31

Come parte del team, ho realizzato molti progetti che prevedevano la sincronizzazione dei dati, quindi dovrei essere competente a rispondere a questa domanda.

La sincronizzazione dei dati è un concetto abbastanza ampio e ci sono troppe cose da discutere. Copre una gamma di approcci diversi con i loro lati positivi e negativi. Ecco una delle possibili classificazioni basate su due prospettive: sincrono / asincrono, client / server / peer-to-peer. La sincronizzazione dell'implementazione dipende fortemente da questi fattori, dalla complessità del modello di dati, dalla quantità di dati trasferiti e memorizzati e da altri requisiti. Quindi in ogni caso particolare la scelta dovrebbe essere a favore dell'implementazione più semplice che soddisfi i requisiti dell'app.

Sulla base di una revisione delle soluzioni esistenti esistenti, possiamo delineare diverse principali classi di sincronizzazione, diverse nella granularità degli oggetti soggetti a sincronizzazione:

  • La sincronizzazione di un intero documento o database viene utilizzata in applicazioni basate su cloud, come Dropbox, Google Drive o Yandex.Disk. Quando l'utente modifica e salva un file, la nuova versione del file viene caricata completamente nel cloud, sovrascrivendo la copia precedente. In caso di conflitto, entrambe le versioni dei file vengono salvate in modo che l'utente possa scegliere quale versione è più pertinente.
  • La sincronizzazione di coppie chiave-valore può essere utilizzata in app con una struttura dati semplice, in cui le variabili sono considerate atomiche, ovvero non suddivise in componenti logici. Questa opzione è simile alla sincronizzazione di interi documenti, poiché sia ​​il valore che il documento possono essere sovrascritti completamente. Tuttavia, dal punto di vista dell'utente un documento è un oggetto complesso composto da molte parti, ma una coppia chiave-valore non è che una stringa breve o un numero. Pertanto, in questo caso possiamo usare una strategia più semplice di risoluzione dei conflitti, considerando il valore più rilevante, se è stato l'ultimo a cambiare.
  • La sincronizzazione dei dati strutturati come un albero o un grafico viene utilizzata in applicazioni più sofisticate in cui la quantità di dati è sufficientemente grande da inviare il database nella sua interezza ad ogni aggiornamento. In questo caso, i conflitti devono essere risolti a livello di singoli oggetti, campi o relazioni. Ci concentriamo principalmente su questa opzione.

Quindi, abbiamo raccolto le nostre conoscenze in questo articolo che penso possa essere molto utile a tutti gli interessati all'argomento => Sincronizzazione dei dati nelle app iOS basate su dati di base ( http://blog.denivip.ru/index.php/2014/04 / data-syncing-in-core-data-based-ios-apps /? lang = it )


3
^^^^^^ Questa è di gran lunga la risposta migliore, ragazzi!
hgoebl,

Sono d'accordo, Denis ha parlato molto dell'argomento + i collegamenti agli articoli sono fantastici. Parla anche dell'OT menzionato da Daniel Paull. La risposta di S.Lott è buona, ma molto più approfondita.
Krystian,

28

Ciò di cui hai veramente bisogno è Operational Transform (OT). Ciò può persino soddisfare i conflitti in molti casi.

Questa è ancora un'area attiva di ricerca, ma ci sono implementazioni di vari algoritmi OT in circolazione. Sono stato coinvolto in tali ricerche per un certo numero di anni, quindi fammi sapere se questo percorso ti interessa e sarò felice di metterti a risorse pertinenti.


7
Daniel, un indicatore delle risorse pertinenti sarebbe apprezzato.
Parand,

4
Ho appena riletto l'articolo di Wikipedia. Ha fatto molta strada e ha molti riferimenti pertinenti nella parte inferiore di quella pagina. Ti avrei indicato l'opera di Chengzheng Sun - la sua opera è citata da Wikipedia. en.wikipedia.org/wiki/Operational_transformation . Spero che aiuti!
Daniel Paull,

13

La domanda non è cristallina, ma esaminerei il blocco ottimistico se fossi in te. Può essere implementato con un numero progressivo restituito dal server per ciascun record. Quando un client tenta di salvare nuovamente il record, includerà il numero di sequenza ricevuto dal server. Se il numero di sequenza corrisponde a ciò che è presente nel database al momento della ricezione dell'aggiornamento, l'aggiornamento è consentito e il numero di sequenza viene incrementato. Se i numeri di sequenza non corrispondono, l'aggiornamento non è consentito.


2
I numeri di sequenza sono i tuoi amici qui. Pensa alle code dei messaggi persistenti.
Daniel Paull,

7

Ho creato un sistema come questo per un'app circa 8 anni fa e posso condividere un paio di modi in cui si è evoluta man mano che l'utilizzo dell'app è cresciuto.

Ho iniziato registrando ogni modifica (inserire, aggiornare o eliminare) da qualsiasi dispositivo in una tabella "cronologia". Pertanto, se, ad esempio, qualcuno modifica il proprio numero di telefono nella tabella "contatto", il sistema modificherà il campo contact.phone e aggiungerà anche un record cronologico con action = update, field = phone, record = [ID contatto], valore = [nuovo numero di telefono]. Quindi, ogni volta che un dispositivo si sincronizza, scarica gli elementi della cronologia dall'ultima sincronizzazione e li applica al suo database locale. Questo suona come il modello di "replica delle transazioni" sopra descritto.

Un problema è mantenere gli ID univoci quando è possibile creare oggetti su dispositivi diversi. Non sapevo degli UUID quando ho iniziato questo, quindi ho usato gli ID a incremento automatico e ho scritto un codice contorto che viene eseguito sul server centrale per controllare i nuovi ID caricati dai dispositivi, cambiarli in un ID univoco in caso di conflitto e dire al dispositivo di origine di modificare l'ID nel suo database locale. Il solo cambiamento degli ID dei nuovi record non era poi così male, ma se creo, ad esempio, un nuovo elemento nella tabella dei contatti, quindi creo un nuovo elemento correlato nella tabella degli eventi, ora ho chiavi esterne che devo anche controlla e aggiorna.

Alla fine ho appreso che gli UUID potevano evitarlo, ma a quel punto il mio database stava diventando piuttosto grande e temevo che un'implementazione UUID completa avrebbe creato un problema di prestazioni. Quindi, invece di utilizzare UUID completi, ho iniziato a utilizzare chiavi alfanumeriche di 8 caratteri generate in modo casuale come ID e ho lasciato il mio codice esistente in atto per gestire i conflitti. Da qualche parte tra le mie attuali chiavi di 8 caratteri e i 36 caratteri di un UUID ci deve essere un punto debole che eliminerebbe i conflitti senza inutile gonfiamento, ma dal momento che ho già il codice di risoluzione dei conflitti, non è stata una priorità sperimentare con quello .

Il problema successivo era che la tabella della cronologia era circa 10 volte più grande dell'intero resto del database. Questo rende lo stoccaggio costoso e qualsiasi manutenzione nella tabella della cronologia può essere dolorosa. Mantenere l'intera tabella consente agli utenti di ripristinare qualsiasi modifica precedente, ma ciò ha iniziato a sembrare eccessivo. Quindi ho aggiunto una routine al processo di sincronizzazione in cui se l'elemento della cronologia che un dispositivo ha scaricato l'ultima volta non esiste più nella tabella della cronologia, il server non gli fornisce gli elementi della cronologia recente, ma invece gli fornisce un file contenente tutti i dati per quell'account. Quindi ho aggiunto un cronjob per eliminare gli elementi della cronologia più vecchi di 90 giorni. Ciò significa che gli utenti possono comunque ripristinare le modifiche di meno di 90 giorni e, se si sincronizzano almeno una volta ogni 90 giorni, gli aggiornamenti saranno incrementali come prima. Ma se aspettano più di 90 giorni,

Questa modifica ha ridotto le dimensioni della tabella della cronologia di quasi il 90%, quindi ora la manutenzione della tabella della cronologia rende il database solo due volte più grande invece di dieci volte più grande. Un altro vantaggio di questo sistema è che la sincronizzazione potrebbe ancora funzionare senza la tabella cronologica, se necessario, come se avessi bisogno di un po 'di manutenzione che lo metteva temporaneamente offline. Oppure potrei offrire diversi periodi di rollback per gli account a prezzi diversi. E se ci sono più di 90 giorni di modifiche da scaricare, il file completo è generalmente più efficiente del formato incrementale.

Se dovessi ricominciare da capo oggi, salterei il controllo dei conflitti ID e mirerei solo a una lunghezza della chiave sufficiente per eliminare i conflitti, con un qualche tipo di controllo degli errori nel caso. Ma la tabella della cronologia e la combinazione di download incrementali per gli aggiornamenti recenti o un download completo quando necessario ha funzionato bene.


1

Per la sincronizzazione delta (modifica), è possibile utilizzare il modello pubsub per pubblicare le modifiche su tutti i client sottoscritti, servizi come pusher possono farlo.

Per il mirroring del database, alcuni framework Web utilizzano un mini database locale per sincronizzare il database lato server con quello locale nel database del browser, è supportata la sincronizzazione parziale. Controlla meteror .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.