HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

La prossima conferenza HighLoad++ si terrà il 6 e 7 aprile 2020 a San Pietroburgo.
Dettagli e biglietti collegamento. HighLoad++ Siberia 2019. Padiglione "Krasnoyarsk". 25 giugno, 12:00. Tesi e presentazione.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Succede che i requisiti pratici siano in conflitto con la teoria, dove gli aspetti importanti per un prodotto commerciale non vengono presi in considerazione. Questo discorso presenta un processo per selezionare e combinare diversi approcci alla creazione di componenti di coerenza causale basati sulla ricerca accademica basata sui requisiti di un prodotto commerciale. Gli ascoltatori apprenderanno gli approcci teorici esistenti agli orologi logici, al monitoraggio delle dipendenze, alla sicurezza del sistema, alla sincronizzazione degli orologi e al motivo per cui MongoDB ha optato per determinate soluzioni.

Mikhail Tyulenev (di seguito denominato MT): – Parlerò della coerenza causale: questa è una funzionalità su cui abbiamo lavorato in MongoDB. Lavoro in un gruppo di sistemi distribuiti, lo abbiamo fatto circa due anni fa.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Nel frattempo, ho dovuto familiarizzare con molte ricerche accademiche, perché questa caratteristica è stata studiata abbastanza bene. Si è scoperto che non un singolo articolo rientra in quanto richiesto in un database di produzione a causa di requisiti molto specifici che probabilmente sono presenti in qualsiasi applicazione di produzione.

Parlerò di come noi, come consumatori di ricerca accademica, prepariamo qualcosa da essa che possiamo poi presentare ai nostri utenti come un piatto già pronto, comodo e sicuro da usare.

Coerenza causale. Definiamo i concetti

Per cominciare, voglio dire in termini generali cos'è la coerenza causale. Ci sono due personaggi: Leonard e Penny (serie TV "The Big Bang Theory"):

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Diciamo che Penny è in Europa e Leonard vuole organizzarle una festa a sorpresa. E non riesce a pensare a niente di meglio che buttarla fuori dalla sua lista di amici, inviando a tutti i suoi amici un aggiornamento sul feed: "Facciamo felice Penny!" (lei è in Europa, mentre dorme, tutto questo non lo vede e non può vederlo, perché non c'è). Alla fine cancella questo post, lo cancella dal Feed e ripristina l'accesso in modo che non si accorga di nulla e non ci sia scandalo.
Tutto questo va bene, ma supponiamo che il sistema sia distribuito e che le cose siano andate un po' male. Può succedere, ad esempio, che la restrizione dell'accesso di Penny sia avvenuta dopo la pubblicazione di questo post, se gli eventi non sono collegati da causa ed effetto. In realtà, questo è un esempio di quando è richiesta la coerenza causale per svolgere una funzione aziendale (in questo caso).

In realtà, queste sono proprietà abbastanza non banali del database: pochissime persone le supportano. Passiamo ai modelli.

Modelli di coerenza

Che cos'è esattamente un modello di coerenza nei database? Queste sono alcune delle garanzie che un sistema distribuito fornisce su quali dati il ​​client può ricevere e in quale sequenza.

In linea di principio, tutti i modelli di coerenza si riducono a quanto un sistema distribuito sia simile a un sistema che gira, ad esempio, su un nodo su un laptop. Un sistema che gira su migliaia di “nodi” geo-distribuiti è così simile a un laptop, nel quale tutte queste proprietà vengono eseguite in linea di principio automaticamente.

Pertanto, i modelli di coerenza vengono applicati solo ai sistemi distribuiti. Tutti i sistemi che esistevano in precedenza e funzionavano con la stessa scala verticale non presentavano tali problemi. C'era una Buffer Cache e da essa veniva sempre letto tutto.

Modello Forte

In realtà, il primo modello è Forte (o la linea di abilità di ascesa, come viene spesso chiamata). Si tratta di un modello di coerenza che garantisce che ogni modifica, una volta confermato che è avvenuta, sia visibile a tutti gli utenti del sistema.

Ciò crea un ordine globale di tutti gli eventi nel database. Questa è una proprietà di consistenza molto forte ed è generalmente molto costosa. Tuttavia, è molto ben supportato. È solo molto costoso e lento, è solo usato raramente. Questa si chiama capacità di ascesa.

C'è un'altra proprietà più forte supportata in Spanner, chiamata Coerenza Esterna. Ne parleremo un po' più tardi.

Causale

Il prossimo è Causale, che è esattamente ciò di cui stavo parlando. Ci sono molti altri sottolivelli tra Forte e Causale di cui non parlerò, ma si riducono tutti a Causale. Questo è un modello importante perché è il più forte di tutti i modelli, la più forte coerenza in presenza di una rete o di partizioni.

Le causali sono in realtà una situazione in cui gli eventi sono collegati da una relazione di causa-effetto. Molto spesso vengono percepiti come Leggi i tuoi diritti dal punto di vista del cliente. Se il cliente ha osservato alcuni valori, non può vedere i valori che erano nel passato. Sta già iniziando a vedere le letture dei prefissi. Si riduce tutto alla stessa cosa.
Le causali come modello di coerenza sono un ordinamento parziale degli eventi sul server, in cui gli eventi di tutti i client vengono osservati nella stessa sequenza. In questo caso, Leonard e Penny.

eventuale

Il terzo modello è la coerenza finale. Questo è ciò che supportano assolutamente tutti i sistemi distribuiti, il modello minimo che abbia senso. Significa quanto segue: quando abbiamo dei cambiamenti nei dati, ad un certo punto diventano coerenti.

In quel momento non dice nulla, altrimenti si trasformerebbe in Coerenza Esterna: sarebbe tutta un'altra storia. Tuttavia, questo è un modello molto popolare, il più comune. Per impostazione predefinita, tutti gli utenti dei sistemi distribuiti utilizzano Eventual Consistency.

Voglio fare alcuni esempi comparativi:

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Cosa significano queste frecce?

  • Latenza. Man mano che la forza di coerenza aumenta, diventa più grande per ovvi motivi: è necessario creare più record, ottenere conferma da tutti gli host e nodi che partecipano al cluster che i dati sono già presenti. Di conseguenza, Eventual Consistency ha la risposta più rapida, perché lì, di regola, puoi persino memorizzarlo e questo, in linea di principio, sarà sufficiente.
  • Disponibilità. Se intendiamo questo come la capacità del sistema di rispondere in presenza di interruzioni della rete, partizioni o qualche tipo di guasto, la tolleranza agli errori aumenta man mano che diminuisce il modello di coerenza, poiché per noi è sufficiente che un host viva e allo stesso tempo il tempo produce alcuni dati. L'eventuale coerenza non garantisce assolutamente nulla sui dati: può essere qualsiasi cosa.
  • Anomalie. Allo stesso tempo, ovviamente, aumenta il numero di anomalie. Nella Coerenza Forte quasi non dovrebbero esistere affatto, ma nella Coerenza Eventuale possono essere qualsiasi cosa. Sorge la domanda: perché le persone scelgono l’Eventuale Coerenza se contiene anomalie? La risposta è che i modelli di Eventual Consistency sono applicabili ed esistono anomalie, ad esempio, in un breve periodo di tempo; è possibile utilizzare la procedura guidata per leggere e leggere più o meno dati coerenti; Spesso è possibile utilizzare modelli di coerenza forte. In pratica funziona e spesso il numero di anomalie è limitato nel tempo.

Teorema del CAP

Quando vedi le parole coerenza, disponibilità, cosa ti viene in mente? Esatto: teorema CAP! Ora voglio sfatare il mito... Non sono io, è Martin Kleppmann, che ha scritto un articolo meraviglioso, un libro meraviglioso.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Il teorema CAP è un principio formulato negli anni 2000 secondo il quale Coerenza, Disponibilità, Partizioni: prendine due qualsiasi e non puoi sceglierne tre. Era un certo principio. Fu dimostrato come teorema pochi anni dopo da Gilbert e Lynch. Quindi questo cominciò ad essere usato come mantra: i sistemi iniziarono a essere divisi in CA, CP, AP e così via.

Questo teorema è stato effettivamente dimostrato nei seguenti casi... Innanzitutto, la Disponibilità non è stata considerata come un valore continuo da zero a centinaia (0 - il sistema è “morto”, 100 - risponde rapidamente; siamo abituati a considerarlo così) , ma come una proprietà dell'algoritmo , che garantisce che per tutte le sue esecuzioni restituisca dati.

Non c'è una parola sul tempo di risposta! Esiste un algoritmo che restituisce i dati dopo 100 anni: un algoritmo disponibile assolutamente meraviglioso, che fa parte del teorema CAP.
Secondo: il teorema è stato dimostrato per cambiamenti nei valori della stessa chiave, nonostante tali cambiamenti siano ridimensionabili. Ciò significa che in realtà non vengono praticamente utilizzati, perché i modelli sono diversi. Consistenza Eventuale, Coerenza Forte (forse).

A cosa serve tutto questo? Inoltre, il teorema CAP esattamente nella forma in cui è stato dimostrato non è praticamente applicabile e viene utilizzato raramente. In forma teorica, in qualche modo limita tutto. Risulta un certo principio che è intuitivamente corretto, ma in generale non è stato dimostrato.

La coerenza causale è il modello più forte

Ciò che sta accadendo ora è che puoi ottenere tutte e tre le cose: coerenza e disponibilità utilizzando le partizioni. In particolare, la coerenza causale è il modello di coerenza più forte, che funziona ancora in presenza di partizioni (interruzioni nella rete). Ecco perché è di così grande interesse, ed è per questo che l'abbiamo ripreso.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

In primo luogo, semplifica il lavoro degli sviluppatori di applicazioni. In particolare, la presenza di un grande supporto da parte del server: quando è garantito che tutti i record che si verificano all'interno di un client arrivino nella stessa sequenza su un altro client. In secondo luogo, resiste alle partizioni.

Cucina interna MongoDB

Ricordandoci che è pranzo ci spostiamo in cucina. Ti parlerò del modello di sistema, ovvero di cos'è MongoDB per coloro che sentono parlare di un database del genere per la prima volta.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

MongoDB (di seguito denominato “MongoDB”) è un sistema distribuito che supporta lo scaling orizzontale, ovvero lo sharding; e all'interno di ogni frammento supporta anche la ridondanza dei dati, ovvero la replica.

Lo sharding in MongoDB (non un database relazionale) esegue il bilanciamento automatico, ovvero ogni raccolta di documenti (o "tabella" in termini di dati relazionali) viene divisa in pezzi e il server li sposta automaticamente tra gli shard.

Il Query Router, che distribuisce le richieste, per un client è un client attraverso il quale funziona. Sa già dove e quali dati si trovano e indirizza tutte le richieste allo shard corretto.

Altro punto importante: MongoDB è un master unico. Esiste un Primario: può accettare record che supportano le chiavi in ​​esso contenute. Non è possibile eseguire la scrittura multimaster.

Abbiamo realizzato la versione 4.2: lì sono apparse nuove cose interessanti. In particolare, hanno inserito Lucene - search - cioè l'eseguibile java direttamente in Mongo, e lì è diventato possibile effettuare ricerche tramite Lucene, come in Elastica.

E hanno realizzato un nuovo prodotto: Charts, disponibile anche su Atlas (il cloud di Mongo). Hanno un livello gratuito: puoi giocarci. Mi è piaciuto molto Charts: visualizzazione dei dati, molto intuitiva.

Ingredienti Consistenza causale

Ho contato circa 230 articoli pubblicati su questo argomento - di Leslie Lampert. Ora dalla mia memoria ti trasmetterò alcune parti di questi materiali.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Tutto è iniziato con un articolo di Leslie Lampert, scritto negli anni '1970. Come puoi vedere, alcune ricerche su questo argomento sono ancora in corso. Ora la coerenza causale sta suscitando interesse in relazione allo sviluppo di sistemi distribuiti.

Restrizioni

Quali restrizioni ci sono? Questo in realtà è uno dei punti principali, perché le restrizioni che un sistema produttivo impone sono molto diverse dalle restrizioni che esistono negli articoli accademici. Spesso sono piuttosto artificiali.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

  • Innanzitutto “MongoDB” è un master unico, come ho già detto (questo semplifica notevolmente).
  • Crediamo che il sistema dovrebbe supportare circa 10mila frammenti. Non possiamo prendere alcuna decisione architetturale che limiti esplicitamente questo valore.
  • Abbiamo un cloud, ma presumiamo che una persona debba ancora avere l'opportunità quando scarica il binario, lo esegue sul suo laptop e tutto funziona alla grande.
  • Diamo per scontato qualcosa che la ricerca raramente dà per scontato: i clienti esterni possono fare quello che vogliono. MongoDB è open source. Di conseguenza, i clienti possono essere così intelligenti e arrabbiati da voler rompere tutto. Ipotizziamo che i Feilors bizantini possano avere origine.
  • Per i client esterni che si trovano all'esterno del perimetro esiste un'importante limitazione: se questa funzionalità è disabilitata, non si dovrebbe osservare alcun degrado delle prestazioni.
  • Un altro punto è generalmente antiaccademico: la compatibilità delle versioni precedenti e di quelle future. I vecchi driver devono supportare i nuovi aggiornamenti e il database deve supportare i vecchi driver.

In generale, tutto ciò impone restrizioni.

Componenti della coerenza causale

Parlerò ora di alcuni componenti. Se consideriamo la coerenza causale in generale, possiamo selezionare i blocchi. Abbiamo scelto tra lavori che appartengono a un determinato blocco: monitoraggio delle dipendenze, scelta degli orologi, come questi orologi possono essere sincronizzati tra loro e come garantiamo la sicurezza: questo è uno schema approssimativo di ciò di cui parlerò:

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Monitoraggio completo delle dipendenze

Perché è necessario? In modo che quando i dati vengono replicati, ogni record, ogni modifica dei dati contiene informazioni sulle modifiche da cui dipende. Il primo e ingenuo cambiamento avviene quando ogni messaggio che contiene un record contiene informazioni sui messaggi precedenti:

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

In questo esempio, il numero tra parentesi graffe rappresenta il numero del record. A volte questi record con valori vengono trasferiti anche nella loro interezza, a volte vengono trasferite alcune versioni. La conclusione è che ogni modifica contiene informazioni su quella precedente (ovviamente porta tutto questo dentro di sé).

Perché abbiamo deciso di non utilizzare questo approccio (monitoraggio completo)? Ovviamente, perché questo approccio non è pratico: qualsiasi modifica a un social network dipende da tutte le modifiche precedenti apportate a quel social network, trasferendo, ad esempio, Facebook o VKontakte in ogni aggiornamento. Tuttavia, ci sono molte ricerche sul Full Dependency Tracking: si tratta di reti pre-sociali; per alcune situazioni funziona davvero.

Monitoraggio esplicito delle dipendenze

Il successivo è più limitato. Qui viene considerato anche il trasferimento di informazioni, ma solo quello che è chiaramente dipendente. Ciò dipende da ciò che, di norma, è determinato dall'Applicazione. Quando i dati vengono replicati, la query restituisce risposte solo quando le dipendenze precedenti sono state soddisfatte, ovvero visualizzate. Questa è l’essenza di come funziona la coerenza causale.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Vede che il record 5 dipende dai record 1, 2, 3, 4 - di conseguenza, attende prima che il client abbia accesso alle modifiche apportate dalla decisione di accesso di Penny, quando tutte le modifiche precedenti sono già passate attraverso il database.

Anche questo non va bene per noi, perché ci sono ancora troppe informazioni e questo rallenterebbe le cose. C'è un altro approccio...

Orologio Lamport

Sono molto vecchi. Lamport Clock significa che queste dipendenze sono ripiegate in una funzione scalare, chiamata Lamport Clock.

Una funzione scalare è un numero astratto. Viene spesso chiamato tempo logico. Ad ogni evento questo contatore aumenta. Counter, attualmente noto al processo, invia ciascun messaggio. È chiaro che i processi possono non essere sincronizzati, possono avere tempi completamente diversi. Tuttavia, il sistema in qualche modo bilancia l’orologio con tali messaggi. Cosa succede in questo caso?

Ho diviso quel grosso frammento in due per renderlo chiaro: gli amici possono vivere in un nodo, che contiene un pezzo della raccolta, e il feed può vivere in un altro nodo, che contiene un pezzo di questa raccolta. È chiaro come possono uscire dalla linea? Il primo feed dirà: "Replicato", quindi Amici. Se il sistema non fornisce una sorta di garanzia che il Feed non verrà mostrato finché non verranno consegnate anche le dipendenze di Friends nella raccolta Friends, allora avremo esattamente la situazione che ho menzionato.

Vedi come aumenta logicamente il tempo del contatore sul Feed:

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Quindi la proprietà principale di questo Orologio Lamport e la coerenza causale (spiegata attraverso Orologio Lamport) è questa: se abbiamo Eventi A e B, e l'Evento B dipende dall'Evento A*, allora ne consegue che il Tempo Logico dell'Evento A è minore di LogicalTime dall'evento B.

* A volte dicono anche che A è accaduto prima di B, cioè A è accaduto prima di B: questa è una certa relazione che ordina parzialmente l'intero insieme di eventi accaduti in generale.

Non è corretto il contrario. Questo è in realtà uno dei principali svantaggi dell'orologio Lamport: l'ordine parziale. Esiste un concetto di eventi simultanei, cioè eventi in cui né (A è accaduto prima di B) né (A è accaduto prima di B). Un esempio potrebbe essere l’aggiunta parallela da parte di Leonard di qualcun altro come amico (nemmeno Leonard, ma Sheldon, per esempio).
Questa è la proprietà che viene spesso utilizzata quando si lavora con gli orologi Lamport: guardano specificamente la funzione e da questo concludono che forse questi eventi sono dipendenti. Perché un modo è vero: se il Tempo Logico A è inferiore al Tempo Logico B, allora B non può verificarsi prima di A; e se di più, allora forse.

Orologio vettoriale

Lo sviluppo logico dell'orologio Lamport è il Vector Clock. Differiscono in quanto ogni nodo qui contiene il proprio orologio separato e vengono trasmessi come vettore.
In questo caso, vedi che l'indice zero del vettore è responsabile del Feed e il primo indice del vettore è degli Amici (ciascuno di questi nodi). E ora aumenteranno: l’indice zero di “Feed” aumenta quando si scrive – 1, 2, 3:

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Perché Vector Clock è migliore? Perché permettono di capire quali eventi sono simultanei e quando si verificano su nodi diversi. Questo è molto importante per un sistema di sharding come MongoDB. Tuttavia non abbiamo scelto questo, anche se è una cosa meravigliosa, funziona benissimo e probabilmente ci farebbe al caso nostro...

Se disponiamo di 10mila frammenti, non possiamo trasferire 10mila componenti, anche se li comprimiamo o inventiamo qualcos'altro: il carico utile sarà comunque molte volte inferiore al volume dell'intero vettore. Pertanto, stringendo cuore e denti, abbiamo abbandonato questo approccio e siamo passati ad un altro.

Chiave TrueTime. Orologio atomico

Avevo detto che ci sarebbe stata una storia su Spanner. Questa è una cosa interessante, uscita direttamente dal XNUMX° secolo: orologi atomici, sincronizzazione GPS.

Qual è l'idea? "Spanner" è un sistema Google che recentemente è diventato persino disponibile per le persone (gli hanno aggiunto SQL). Ogni transazione ha un timestamp. Poiché il tempo è sincronizzato*, a ogni evento può essere assegnato un tempo specifico: gli orologi atomici hanno un tempo di attesa, dopo il quale è garantito che "accada" un tempo diverso.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

In questo modo, semplicemente scrivendo nel database e attendendo un certo periodo di tempo, viene automaticamente garantita la serializzabilità dell'evento. Hanno il modello di Coerenza più forte che si possa immaginare in linea di principio: è la Coerenza Esterna.

* Questo è il problema principale con gli orologi Lampart: non sono mai sincroni sui sistemi distribuiti. Possono divergere; anche con NTP, non funzionano ancora molto bene. "Spanner" ha un orologio atomico e la sincronizzazione, a quanto pare, è di microsecondi.

Perché non abbiamo scelto? Non presupponiamo che i nostri utenti abbiano un orologio atomico integrato. Quando appariranno, integrati in ogni laptop, ci sarà una sorta di fantastica sincronizzazione GPS - allora sì... Ma per ora il meglio che è possibile è Amazon, Stazioni base - per i fanatici... Quindi abbiamo usato altri orologi .

Orologio ibrido

Questo è in realtà ciò che funziona in MongoDB quando si garantisce la coerenza causale. Come sono ibridi? L'ibrido è un valore scalare, ma ha due componenti:

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

  • Il primo è l'era Unix (quanti secondi sono trascorsi dall'inizio del mondo dei computer).
  • Il secondo è un incremento, anch'esso un int senza segno a 32 bit.

Questo è tutto, in realtà. Esiste questo approccio: la parte responsabile del tempo è sempre sincronizzata con l'orologio; ogni volta che si verifica un aggiornamento, questa parte viene sincronizzata con l'orologio e risulta che l'ora è sempre più o meno corretta, e l'incremento consente di distinguere tra eventi che si sono verificati nello stesso momento.

Perché questo è importante per MongoDB? Perché ti consente di creare una sorta di ristoranti di backup in un determinato momento, ovvero l'evento è indicizzato in base al tempo. Questo è importante quando sono necessari determinati eventi; Per un database, gli eventi sono modifiche nel database che si sono verificate a determinati intervalli di tempo.

Il motivo più importante te lo dirò solo a te (per favore, non dirlo a nessuno)! Lo abbiamo fatto perché questo è l'aspetto dei dati organizzati e indicizzati in MongoDB OpLog. OpLog è una struttura dati che contiene assolutamente tutte le modifiche nel database: vanno prima a OpLog e poi vengono applicate allo Storage stesso nel caso in cui si tratti di una data o di un frammento replicato.

Questo era il motivo principale. Tuttavia, ci sono anche requisiti pratici per lo sviluppo di un database, il che significa che dovrebbe essere semplice: poco codice, il minor numero possibile di cose rotte che devono essere riscritte e testate. Il fatto che i nostri oplog fossero indicizzati da orologi ibridi ci ha aiutato molto e ci ha permesso di fare la scelta giusta. Ha davvero dato i suoi frutti e in qualche modo ha funzionato magicamente sul primissimo prototipo. È stato molto bello!

Sincronizzazione dell'orologio

Esistono diversi metodi di sincronizzazione descritti nella letteratura scientifica. Sto parlando di sincronizzazione quando abbiamo due frammenti diversi. Se è presente un set di repliche, non è necessaria alcuna sincronizzazione: si tratta di un “master singolo”; abbiamo un OpLog, in cui rientrano tutte le modifiche - in questo caso, tutto è già ordinato in sequenza nell'“Oplog” stesso. Ma se abbiamo due frammenti diversi, la sincronizzazione temporale è importante qui. È qui che l'orologio vettoriale ha aiutato di più! Ma non li abbiamo.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Il secondo è adatto: questo è "Heartbeats". È possibile scambiare alcuni segnali che si verificano ogni unità di tempo. Ma i battiti cardiaci sono troppo lenti, non possiamo fornire latenza al nostro client.

Il vero tempo è, ovviamente, una cosa meravigliosa. Ma, ancora una volta, questo è probabilmente il futuro... Anche se è già possibile farlo in Atlas, esistono già sincronizzatori orari veloci “Amazon”. Ma non sarà disponibile per tutti.

Il pettegolezzo è quando tutti i messaggi includono il tempo. Questo è approssimativamente ciò che usiamo. Ogni messaggio tra i nodi, un driver, un router del nodo dati, assolutamente tutto per MongoDB è una sorta di elemento, un componente del database che contiene un orologio che funziona. Hanno ovunque il significato del tempo ibrido, si trasmette. 64 bit? Questo lo permette, questo è possibile.

Come funziona tutto insieme?

Qui sto guardando un set di repliche per renderlo un po' più semplice. Ci sono Primarie e Secondarie. Il secondario esegue la replica e non è sempre completamente sincronizzato con il primario.

Un inserimento avviene nel “Primery” con un certo valore temporale. Questo inserto aumenta il conteggio interno di 11, se questo è il massimo. Oppure controllerà i valori dell'orologio e si sincronizzerà con l'orologio se i valori dell'orologio sono maggiori. Ciò ti consente di organizzare in base al tempo.

Dopo aver effettuato la registrazione, si verifica un momento importante. L'orologio è in "MongoDB" e viene incrementato solo in caso di scrittura su "Oplog". Questo è l’evento che cambia lo stato del sistema. In assoluto tutti gli articoli classici, un evento viene considerato quando un messaggio colpisce un nodo: il messaggio è arrivato, il che significa che il sistema ha cambiato stato.

Ciò è dovuto al fatto che durante la ricerca non è del tutto chiaro come verrà interpretato questo messaggio. Sappiamo per certo che se non si riflette nell'“Oplog”, non verrà interpretato in alcun modo e un cambiamento nello stato del sistema è solo una voce nell'“Oplog”. Questo ci semplifica tutto: il modello lo semplifica, e ci permette di organizzarlo all'interno di un set di repliche, e tante altre cose utili.

Viene restituito il valore che è già scritto in "Oplog": sappiamo che "Oplog" contiene già questo valore e il suo tempo è 12. Ora, diciamo, la lettura inizia da un altro nodo (Secondario) e trasmette afterClusterTime in il messaggio. Dice: "Ho bisogno di tutto quello che è successo almeno dopo le 12 o durante le dodici" (vedi foto sopra).

Questo è ciò che viene chiamato Causal a consistent (CAT). In teoria esiste un tale concetto che questa è una sorta di intervallo di tempo, che è di per sé coerente. In questo caso possiamo dire che questo è lo stato del sistema osservato al tempo 12.

Ora non c'è ancora nulla qui, perché questo tipo di simula la situazione in cui è necessario che il Secondario replichi i dati dal Primario. Aspetta... E ora i dati sono arrivati: restituisce questi valori.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Funziona più o meno così. Quasi.

Cosa significa "quasi"? Supponiamo che ci sia qualcuno che abbia letto e capito come funziona tutto questo. Mi sono reso conto che ogni volta che si verifica ClusterTime, aggiorna l'orologio logico interno e quindi la voce successiva aumenta di uno. Questa funzione richiede 20 righe. Diciamo che questa persona trasmette il numero più grande a 64 bit, meno uno.

Perché "meno uno"? Poiché l'orologio interno verrà sostituito con questo valore (ovviamente, questo è il più grande possibile e maggiore dell'ora corrente), verrà inserita una voce in "Oplog" e l'orologio verrà incrementato di un'altra unità - e ci sarà già essere un valore massimo (semplicemente ci sono tutte le unità, non c'è nessun altro posto dove andare), unsaint ints).

È chiaro che dopo questo il sistema diventa assolutamente inaccessibile a qualsiasi cosa. Può solo essere scaricato e pulito: molto lavoro manuale. Disponibilità completa:

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Inoltre, se questo viene replicato da qualche altra parte, l’intero cluster semplicemente crolla. Una situazione assolutamente inaccettabile che chiunque può organizzare in modo molto semplice e veloce! Pertanto, abbiamo considerato questo momento come uno dei più importanti. Come prevenirlo?

Il nostro modo è firmare clusterTime

Ecco come viene trasmesso nel messaggio (prima del testo blu). Ma abbiamo anche iniziato a generare una firma (testo blu):

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

La firma è generata da una chiave che viene conservata all'interno del database, all'interno di un perimetro sicuro; stesso viene generato e aggiornato (gli utenti non vedono nulla al riguardo). Viene generato un hash e ciascun messaggio viene firmato al momento della creazione e convalidato al momento della ricezione.
Probabilmente sorge nella mente delle persone la domanda: “Quanto questo rallenta le cose?” Ti avevo detto che dovrebbe funzionare velocemente, soprattutto in assenza di questa funzionalità.

Cosa significa utilizzare la coerenza causale in questo caso? Questo serve per mostrare il parametro afterClusterTime. Senza questo, passerà comunque semplicemente i valori. Il gossiping, a partire dalla versione 3.6, funziona sempre.

Se abbandoniamo la generazione costante di firme, il sistema rallenterà anche in assenza di una funzionalità che non soddisfi i nostri approcci e requisiti. Allora cosa abbiamo fatto?

Fallo velocemente!

È una cosa abbastanza semplice, ma il trucco è interessante: lo condividerò, forse a qualcuno interesserà.
Abbiamo un hash che memorizza i dati firmati. Tutti i dati passano attraverso la cache. La cache non firma l'ora specifica, ma l'intervallo. Quando arriva un valore, generiamo un Range, mascheriamo gli ultimi 16 bit e firmiamo questo valore:

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Ricevendo tale firma acceleriamo il sistema (relativamente) 65mila volte. Funziona benissimo: quando abbiamo effettuato gli esperimenti, il tempo è effettivamente diminuito di 10mila volte quando abbiamo avuto un aggiornamento sequenziale. È chiaro che quando sono in disaccordo, questo non funziona. Ma nella maggior parte dei casi pratici funziona. La combinazione della firma Range con la firma ha risolto il problema di sicurezza.

Cosa abbiamo imparato?

Lezioni che abbiamo imparato da questo:

  • Dobbiamo leggere materiali, storie, articoli, perché abbiamo molte cose interessanti. Quando lavoriamo su qualche funzionalità (soprattutto adesso, quando abbiamo effettuato transazioni, ecc.), dobbiamo leggere e comprendere. Ci vuole tempo, ma in realtà è molto utile perché rende chiaro dove siamo. Non sembrava che avessimo inventato nulla di nuovo, abbiamo solo preso gli ingredienti.

    In generale, c'è una certa differenza nel modo di pensare quando si tiene una conferenza accademica (Sigmon, per esempio): tutti si concentrano su nuove idee. Cosa c'è di nuovo nel nostro algoritmo? Non c'è niente di particolarmente nuovo qui. La novità sta piuttosto nel modo in cui abbiamo fuso insieme gli approcci esistenti. Quindi la prima cosa è leggere i classici, a cominciare da Lampart.

  • Nella produzione, i requisiti sono completamente diversi. Sono sicuro che molti di voi non si trovano di fronte a database “sferici” in un vuoto astratto, ma a cose normali e reali che hanno problemi di disponibilità, latenza e tolleranza agli errori.
  • L'ultima cosa è che abbiamo dovuto considerare idee diverse e combinare insieme diversi articoli completamente diversi in un unico approccio. L'idea della firma, ad esempio, è venuta generalmente da un articolo che considerava il protocollo Paxos, che per i Failor non bizantini è all'interno del protocollo di autorizzazione, per quelli bizantini - al di fuori del protocollo di autorizzazione... In generale, questo è esattamente ciò che noi ha finito per fare.

    Non c'è assolutamente nulla di nuovo qui! Ma appena abbiamo mescolato il tutto... È come dire che la ricetta dell'insalata Olivier è una sciocchezza, perché le uova, la maionese e i cetrioli sono già stati inventati... È più o meno la stessa storia.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Concludo con questo. Grazie!

domande

Domanda del pubblico (di seguito denominata B): – Grazie, Mikhail, per il rapporto! Interessante il tema del tempo. Stai utilizzando Gossiping. Hanno detto che ognuno ha il proprio tempo, ognuno conosce la propria ora locale. A quanto ho capito, abbiamo un driver - possono esserci molti client con driver, anche query-planner, anche shard... E a cosa si riduce il sistema se all'improvviso abbiamo una discrepanza: qualcuno decide che è per un minuto avanti, qualcuno un minuto indietro? Dove finiremo?

MT: – Ottima domanda davvero! Volevo solo parlare di frammenti. Se ho capito bene la domanda, abbiamo la seguente situazione: c'è il frammento 1 e il frammento 2, la lettura avviene da questi due frammenti - hanno una discrepanza, non interagiscono tra loro, perché il tempo che conoscono è diverso, soprattutto il tempo in cui esistono negli oplog.
Diciamo che il frammento 1 ha creato un milione di record, il frammento 2 non ha fatto nulla e la richiesta è arrivata a due frammenti. E il primo ha un afterClusterTime di oltre un milione. In una situazione del genere, come ho spiegato, il frammento 2 non risponderà mai.

A: – Volevo sapere come si sincronizzano e scelgono un orario logico?

MT: - Molto facile da sincronizzare. Shard, quando afterClusterTime arriva da lui e non trova il tempo nell'"Oplog", non avvia alcuna approvazione. Cioè, alza il suo tempo con le mani a questo valore. Ciò significa che non ci sono eventi che corrispondono a questa richiesta. Crea questo evento artificialmente e quindi diventa Causale Coerente.

A: – E se poi gli arrivassero altri eventi che sono andati persi da qualche parte nella rete?

MT: – Shard è progettato in modo tale che non verranno più, poiché è un unico master. Se si è già iscritto, non verranno, ma arriveranno più tardi. Non può succedere che qualcosa si blocchi da qualche parte, poi non scriva più e poi arrivino questi eventi - e la coerenza causale si rompe. Quando non scrive, dovrebbero venire tutti dopo (li aspetterà).

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

A: – Ho diverse domande riguardo alle code. La coerenza causale presuppone che esista una coda specifica di azioni che devono essere eseguite. Cosa succede se uno dei nostri pacchi scompare? Arriva il 10, l'11... il 12 è scomparso e tutti gli altri aspettano che diventi realtà. E all'improvviso la nostra macchina è morta, non possiamo fare nulla. Esiste una lunghezza massima della coda che si accumula prima di essere eseguita? Quale fallimento fatale si verifica quando si perde uno stato? Inoltre, se scriviamo che esiste uno stato precedente, allora dovremmo in qualche modo iniziare da esso? Ma non lo hanno respinto!

MT: – Anche questa è un'ottima domanda! Che cosa stiamo facendo? MongoDB ha il concetto di quorum scrive, quorum legge. In quali casi è possibile perdere un messaggio? Quando una scrittura non è il quorum o quando una lettura non è il quorum (potrebbe anche rimanere qualche tipo di spazzatura).
Per quanto riguarda la coerenza causale, è stato effettuato un ampio test sperimentale, il cui risultato è stato che nel caso in cui le scritture e le letture non sono quorum, si verificano violazioni della coerenza causale. Esattamente quello che dici!

Il nostro consiglio: utilizzare almeno la lettura del quorum quando si utilizza la coerenza causale. In questo caso non si perde nulla, anche se si perde il record del quorum... Questa è una situazione ortogonale: se l'utente non vuole che i dati vadano persi, deve utilizzare un record del quorum. La coerenza causale non garantisce la durabilità. La durabilità è garantita dalla replica e dai macchinari associati alla replica.

A: – Quando creiamo un’istanza che esegue lo sharding per noi (rispettivamente non master, ma slave), si affida al tempo Unix della propria macchina o al tempo del “master”; Si sincronizza per la prima volta o periodicamente?

MT: – Adesso chiarisco. Frammento (cioè partizione orizzontale): lì c'è sempre un Primario. E un frammento può avere un “master” e possono esserci repliche. Ma il frammento supporta sempre la registrazione, perché deve supportare qualche dominio (il frammento ha Primario).

A: – Quindi tutto dipende esclusivamente dal “maestro”? Viene sempre utilizzato il tempo principale?

MT: - SÌ. Si può dire in senso figurato: il tempo stringe quando si verifica un ingresso nel "master", nell'"Oplog".

A: – Abbiamo un cliente che si connette e non ha bisogno di sapere nulla sull’ora?

MT: – Non hai bisogno di sapere assolutamente nulla! Se parliamo di come funziona sul cliente: quando il cliente vuole usare la coerenza causale, deve aprire una sessione. Ora è tutto lì: transazioni nella sessione e recupero di diritti... Una sessione è l'ordinamento di eventi logici che si verificano con il client.

Se apre questa sessione e dice che desidera la coerenza causale (se la sessione supporta la coerenza causale per impostazione predefinita), tutto funziona automaticamente. L'autista ricorda questo tempo e lo aumenta quando riceve un nuovo messaggio. Ricorda quale risposta ha restituito il precedente dal server che ha restituito i dati. La richiesta successiva conterrà afterCluster("tempo maggiore di questo").

Il cliente non ha bisogno di sapere assolutamente nulla! Questo gli è completamente opaco. Se le persone utilizzano queste funzionalità, cosa possono fare? Innanzitutto, puoi leggere in sicurezza i secondari: puoi scrivere su Primario e leggere da secondari replicati geograficamente ed essere sicuro che funzioni. Allo stesso tempo, le sessioni registrate su Primario possono anche essere trasferite su Secondario, ad es. puoi utilizzare non una sessione, ma diverse.

A: – Un nuovo livello di scienza informatica – i tipi di dati CRDT (Conflict-free Replicated Data Types) – è fortemente correlato al tema della coerenza eventuale. Hai considerato l'integrazione di questo tipo di dati nel database e cosa puoi dire al riguardo?

MT: - Buona domanda! CRDT ha senso per i conflitti di scrittura: in MongoDB, master singolo.

A: – Ho una domanda da parte dei devops. Nel mondo reale, ci sono situazioni gesuitiche in cui si verifica il fallimento bizantino e le persone malvagie all'interno del perimetro protetto iniziano a intrufolarsi nel protocollo, inviando pacchi artigianali in un modo speciale?

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

MT: – Le persone malvagie all’interno del perimetro sono come un cavallo di Troia! Le persone malvagie all'interno del perimetro possono fare molte cose cattive.

A: – È chiaro che lasciare, grosso modo, un buco nel server attraverso il quale poter mettere uno zoo di elefanti e far crollare l'intero ammasso per sempre... Ci vorrà del tempo per il ripristino manuale... Questo, per usare un eufemismo, è sbagliato. D'altronde questo è interessante: nella vita reale, in pratica, ci sono situazioni in cui si verificano naturalmente attacchi interni simili?

MT: – Poiché raramente incontro violazioni della sicurezza nella vita reale, non posso dire se si verificano. Ma se parliamo della filosofia di sviluppo, la pensiamo così: abbiamo un perimetro che fornisce sicurezza a coloro che si occupano della sicurezza: questo è un castello, un muro; e all'interno del perimetro puoi fare quello che vuoi. È chiaro che ci sono utenti con la possibilità solo di visualizzare e altri con la possibilità di cancellare la directory.

A seconda dei diritti, il danno che gli utenti possono fare può essere un topo o un elefante. È chiaro che un utente con tutti i diritti può fare qualsiasi cosa. Un utente con diritti limitati può causare danni significativamente inferiori. In particolare, non può rompere il sistema.

A: – Nel perimetro protetto, qualcuno ha tentato di creare protocolli inaspettati per il server in modo da distruggere completamente il server e, se sei fortunato, l'intero cluster... È mai successo così?

MT: "Non ho mai sentito parlare di queste cose." Il fatto che tu possa mandare in crash un server in questo modo non è un segreto. Fail inside, essendo del protocollo, essendo un utente autorizzato che può scrivere qualcosa del genere nel messaggio... In effetti, è impossibile, perché sarà comunque verificato. È possibile disabilitare questa autenticazione per gli utenti che non la desiderano: questo è un loro problema; loro, in parole povere, hanno distrutto i muri da soli e puoi spingere lì dentro un elefante, che calpesterà... Ma in generale, puoi vestirti da riparatore, vieni a tirarlo fuori!

A: – Grazie per la segnalazione. Sergey (Yandex). C'è una costante in Mong che limita il numero di membri votanti nel Set Replica, e questa costante è 7 (sette). Perché è una costante? Perché questo non è una sorta di parametro?

MT: – Abbiamo set di repliche con 40 nodi. C'è sempre la maggioranza. non so quale versione...

A: – In Replica Set è possibile eseguire membri senza diritto di voto, ma ci sono un massimo di 7 membri votanti.Come possiamo sopravvivere allo shutdown in questo caso se il nostro Replica Set è distribuito su 3 data center? Un data center può spegnersi facilmente e un'altra macchina può cadere.

MT: – Questo va già un po’ oltre lo scopo della relazione. Questa è una domanda generale. Forse posso parlartene più tardi.

HighLoad++, Mikhail Tyulenev (MongoDB): Coerenza causale: dalla teoria alla pratica

Alcuni annunci 🙂

Grazie per stare con noi. Ti piacciono i nostri articoli? Vuoi vedere contenuti più interessanti? Sostienici effettuando un ordine o raccomandando agli amici, cloud VPS per sviluppatori da $ 4.99, un analogo unico dei server entry-level, che è stato inventato da noi per te: Tutta la verità su VPS (KVM) E5-2697 v3 (6 core) 10 GB DDR4 480 GB SSD 1 Gbps da $ 19 o come condividere un server? (disponibile con RAID1 e RAID10, fino a 24 core e fino a 40 GB DDR4).

Dell R730xd 2 volte più economico nel data center Equinix Tier IV ad Amsterdam? Solo qui 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV da $199 In Olanda! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - da $99! Leggi Come costruire Infrastructure Corp. classe con l'utilizzo di server Dell R730xd E5-2650 v4 del valore di 9000 euro per un centesimo?

Fonte: habr.com

Aggiungi un commento