HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

De volgende HighLoad++ conferentie vindt plaats op 6 en 7 april 2020 in St. Petersburg.
Details en kaartjes link. HighLoad++ Siberië 2019. Hal "Krasnojarsk". 25 juni, 12:00 uur. Scripties en presentatie.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Het komt voor dat praktische eisen in strijd zijn met de theorie, waarbij aspecten die belangrijk zijn voor een commercieel product buiten beschouwing worden gelaten. Deze lezing presenteert een proces voor het selecteren en combineren van verschillende benaderingen voor het creëren van componenten voor causale consistentie, gebaseerd op academisch onderzoek gebaseerd op de vereisten van een commercieel product. Luisteraars zullen leren over bestaande theoretische benaderingen van logische klokken, het volgen van afhankelijkheid, systeembeveiliging, kloksynchronisatie en waarom MongoDB voor bepaalde oplossingen heeft gekozen.

Michail Tyulenev (hierna MT genoemd): – Ik zal het hebben over causale consistentie - dit is een functie waaraan we in MongoDB hebben gewerkt. Ik werk in een groep gedistribueerde systemen, we deden het ongeveer twee jaar geleden.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Daarbij moest ik mezelf vertrouwd maken met veel academisch onderzoek, omdat deze functie vrij goed is bestudeerd. Het bleek dat geen enkel artikel past in wat er nodig is in een productiedatabase vanwege de zeer specifieke vereisten die waarschijnlijk in elke productietoepassing aanwezig zijn.

Ik zal het hebben over hoe wij als consumenten van academisch onderzoek er iets van bereiden dat we vervolgens als kant-en-klaar gerecht aan onze gebruikers kunnen presenteren, dat handig en veilig in gebruik is.

Causale consistentie. Laten we de concepten definiëren

Om te beginnen wil ik in algemene termen zeggen wat causale consistentie is. Er zijn twee personages - Leonard en Penny (tv-serie "The Big Bang Theory"):

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Laten we zeggen dat Penny in Europa is en Leonard een verrassingsfeestje voor haar wil geven. En hij kan niets beters bedenken dan haar van zijn vriendenlijst te gooien en al zijn vrienden een update op de feed te sturen: "Laten we Penny blij maken!" (ze is in Europa, terwijl ze slaapt, ze ziet dit allemaal niet en kan het ook niet zien, omdat ze er niet is). Uiteindelijk verwijdert ze dit bericht, wist het uit de feed en herstelt de toegang zodat ze niets merkt en er geen schandaal ontstaat.
Dit is allemaal goed en wel, maar laten we aannemen dat het systeem gedistribueerd is en dat er iets mis is gegaan. Het kan bijvoorbeeld gebeuren dat Penny's toegangsbeperking plaatsvond nadat dit bericht verscheen, als de gebeurtenissen geen oorzaak en gevolg met elkaar in verband staan. Eigenlijk is dit een voorbeeld van wanneer causale consistentie vereist is om een ​​bedrijfsfunctie uit te voeren (in dit geval).

In feite zijn dit nogal niet-triviale eigenschappen van de database - zeer weinig mensen ondersteunen ze. Laten we verder gaan met de modellen.

Consistentiemodellen

Wat is precies een consistentiemodel in databases? Dit zijn enkele van de garanties die een gedistribueerd systeem geeft over welke gegevens de klant kan ontvangen en in welke volgorde.

In principe komen alle consistentiemodellen neer op hoe vergelijkbaar een gedistribueerd systeem is met een systeem dat bijvoorbeeld op één knooppunt op een laptop draait. En dit is hoe vergelijkbaar een systeem dat op duizenden geogedistribueerde “Nodes” draait, lijkt op een laptop, waarin al deze eigenschappen in principe automatisch worden uitgevoerd.

Daarom worden consistentiemodellen alleen toegepast op gedistribueerde systemen. Alle systemen die voorheen bestonden en op dezelfde verticale schaal werkten, ondervonden dergelijke problemen niet. Er was één buffercache en daaruit werd altijd alles gelezen.

Model Sterk

Eigenlijk is het allereerste model Strong (of de stijgingslijn, zoals het vaak wordt genoemd). Dit is een consistentiemodel dat ervoor zorgt dat elke wijziging, zodra bevestigd dat deze heeft plaatsgevonden, zichtbaar is voor alle gebruikers van het systeem.

Hierdoor ontstaat een globale volgorde van alle gebeurtenissen in de database. Dit is een zeer sterke consistentie-eigenschap en is over het algemeen erg duur. Het wordt echter zeer goed ondersteund. Het is alleen erg duur en traag; het wordt slechts zelden gebruikt. Dit wordt stijgvermogen genoemd.

Er is nog een andere, sterkere eigenschap die wordt ondersteund in Spanner: Externe consistentie. We zullen er later over praten.

Causaal

De volgende is Causaal, en dat is precies waar ik het over had. Er zijn nog een aantal subniveaus tussen Sterk en Causaal waar ik het niet over zal hebben, maar ze komen allemaal neer op Causaal. Dit is een belangrijk model omdat het het sterkste van alle modellen is, de sterkste consistentie in de aanwezigheid van een netwerk of partities.

Causaliteiten zijn eigenlijk een situatie waarin gebeurtenissen met elkaar verbonden zijn door een oorzaak-gevolgrelatie. Heel vaak worden ze vanuit het perspectief van de cliënt gezien als Lees uw rechten. Als de cliënt bepaalde waarden heeft waargenomen, kan hij geen waarden zien die er in het verleden waren. Hij begint al voorvoegselwaarden te zien. Het komt allemaal op hetzelfde neer.
Causals als consistentiemodel zijn een gedeeltelijke volgorde van gebeurtenissen op de server, waarbij gebeurtenissen van alle clients in dezelfde volgorde worden waargenomen. In dit geval Leonard en Penny.

Uiteindelijke

Het derde model is uiteindelijke consistentie. Dit is wat absoluut alle gedistribueerde systemen ondersteunen, het minimale model dat überhaupt zinvol is. Het betekent het volgende: als we enkele wijzigingen in de gegevens hebben, worden deze op een gegeven moment consistent.

Op zo'n moment zegt ze niets, anders zou ze veranderen in Externe Consistentie - het zou een heel ander verhaal zijn. Niettemin is dit een zeer populair model, het meest voorkomende. Standaard gebruiken alle gebruikers van gedistribueerde systemen Eventuele consistentie.

Ik wil enkele vergelijkende voorbeelden geven:

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Wat betekenen deze pijlen?

  • Latentie. Naarmate de consistentiesterkte toeneemt, wordt deze om voor de hand liggende redenen groter: u moet meer records maken en bevestiging krijgen van alle hosts en knooppunten die deelnemen aan het cluster dat de gegevens er al zijn. Dienovereenkomstig heeft Uiteindelijke Consistentie het snelste antwoord, omdat je het daar in de regel zelfs in het geheugen kunt vastleggen en dit zal in principe voldoende zijn.
  • Beschikbaarheid. Als we dit opvatten als het vermogen van het systeem om te reageren in geval van netwerkbreuken, partities of een storing, neemt de fouttolerantie toe naarmate het consistentiemodel afneemt, omdat het voor ons voldoende is dat er één host aanwezig is en tegelijkertijd de tijd produceert bepaalde gegevens. Eventuele consistentie garandeert helemaal niets over de gegevens; het kan van alles zijn.
  • Afwijkingen. Tegelijkertijd neemt uiteraard het aantal afwijkingen toe. In Sterke Consistentie zouden ze bijna helemaal niet moeten bestaan, maar in Eventuele Consistentie kunnen ze van alles zijn. De vraag rijst: waarom kiezen mensen voor Uiteindelijke Consistentie als deze afwijkingen bevat? Het antwoord is dat Eventual Consistentie-modellen toepasbaar zijn en dat er bijvoorbeeld in korte tijd afwijkingen optreden; het is mogelijk om de wizard te gebruiken om consistente gegevens te lezen en min of meer te lezen; Vaak is het mogelijk om sterke consistentiemodellen te gebruiken. In de praktijk werkt dit en vaak is het aantal afwijkingen beperkt in de tijd.

CAP-stelling

Wat komt er in je op als je de woorden consistentie en beschikbaarheid ziet? Dat klopt - CAP-stelling! Nu wil ik de mythe verdrijven... Ik ben het niet, het is Martin Kleppmann, die een prachtig artikel heeft geschreven, een prachtig boek.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

De CAP-stelling is een principe dat in de jaren 2000 werd geformuleerd en luidde: Consistentie, Beschikbaarheid, Partities: neem er twee, en je kunt er geen drie kiezen. Het was een bepaald principe. Het werd een paar jaar later als stelling bewezen door Gilbert en Lynch. Toen begon dit als een mantra te worden gebruikt - systemen werden verdeeld in CA, CP, AP enzovoort.

Deze stelling werd feitelijk bewezen voor de volgende gevallen... Ten eerste werd Beschikbaarheid niet beschouwd als een continue waarde van nul tot honderden (0 - het systeem is "dood", 100 - reageert snel; we zijn gewend dit zo te beschouwen) , maar als een eigenschap van het algoritme , dat garandeert dat het voor al zijn uitvoeringen gegevens retourneert.

Er wordt helemaal geen woord gesproken over de responstijd! Er is een algoritme dat na 100 jaar gegevens retourneert - een absoluut prachtig beschikbaar algoritme, dat deel uitmaakt van de CAP-stelling.
Ten tweede: de stelling werd bewezen voor veranderingen in de waarden van dezelfde sleutel, ondanks het feit dat deze veranderingen aanpasbaar zijn. Dit betekent dat ze in werkelijkheid praktisch niet worden gebruikt, omdat de modellen verschillend zijn. Uiteindelijke consistentie, sterke consistentie (misschien).

Waar is dit allemaal voor? Bovendien is de CAP-stelling in precies de vorm waarin deze werd bewezen praktisch niet toepasbaar en wordt zelden gebruikt. In theoretische vorm beperkt het op de een of andere manier alles. Er blijkt een bepaald principe te bestaan ​​dat intuïtief correct is, maar in het algemeen niet bewezen is.

Causale consistentie is het sterkste model

Wat er nu gebeurt, is dat je alle drie de dingen kunt krijgen: consistentie en beschikbaarheid met behulp van partities. Causale consistentie is met name het sterkste consistentiemodel, dat nog steeds werkt in de aanwezigheid van partities (breuken in het netwerk). Daarom is het zo interessant en daarom zijn wij ermee aan de slag gegaan.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Ten eerste vereenvoudigt het het werk van applicatieontwikkelaars. In het bijzonder de aanwezigheid van geweldige ondersteuning vanuit de server: wanneer alle records die binnen de ene client voorkomen gegarandeerd in dezelfde volgorde op een andere client aankomen. Ten tweede is het bestand tegen partities.

MongoDB interne keuken

We herinneren ons dat het lunch is en gaan naar de keuken. Ik zal je vertellen over het systeemmodel, namelijk wat MongoDB is voor degenen die voor het eerst over een dergelijke database horen.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

MongoDB (hierna “MongoDB” genoemd) is een gedistribueerd systeem dat horizontale schaling ondersteunt, dat wil zeggen sharding; en binnen elke shard ondersteunt het ook gegevensredundantie, dat wil zeggen replicatie.

Sharding in MongoDB (geen relationele database) voert automatische balancering uit, dat wil zeggen dat elke verzameling documenten (of "tabel" in termen van relationele gegevens) in stukken wordt verdeeld, en de server deze automatisch tussen shards verplaatst.

De Query Router, die verzoeken voor een client distribueert, is een client waarmee deze werkt. Het weet al waar en welke gegevens zich bevinden en stuurt alle verzoeken naar de juiste shard.

Nog een belangrijk punt: MongoDB is een enkele master. Er is één primair bestand: er kunnen records in staan ​​die de sleutels ondersteunen die het bevat. U kunt niet met meerdere masters schrijven.

We hebben release 4.2 gemaakt - daar verschenen nieuwe interessante dingen. In het bijzonder hebben ze Lucene - zoeken - namelijk uitvoerbare Java rechtstreeks in Mongo ingevoegd, en daar werd het mogelijk om zoekopdrachten uit te voeren via Lucene, net als in Elastica.

En ze hebben een nieuw product gemaakt: Charts, het is ook beschikbaar op Atlas (Mongo's eigen cloud). Ze hebben een gratis niveau, je kunt ermee spelen. Ik hield echt van Grafieken - datavisualisatie, zeer intuïtief.

Ingrediënten Causale consistentie

Ik telde ongeveer 230 artikelen die over dit onderwerp zijn gepubliceerd - van Leslie Lampert. Nu zal ik u vanuit mijn geheugen enkele delen van deze materialen overbrengen.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Het begon allemaal met een artikel van Leslie Lampert, geschreven in de jaren zeventig. Zoals u kunt zien, wordt er nog steeds onderzoek gedaan naar dit onderwerp. Nu ervaart Causale consistentie belangstelling in verband met de ontwikkeling van gedistribueerde systemen.

Beperkingen

Welke beperkingen zijn er? Dit is eigenlijk een van de belangrijkste punten, omdat de beperkingen die een productiesysteem oplegt heel anders zijn dan de beperkingen die in wetenschappelijke artikelen bestaan. Ze zijn vaak behoorlijk kunstmatig.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

  • Ten eerste is "MongoDB" een enkele master, zoals ik al zei (dit vereenvoudigt enorm).
  • Wij zijn van mening dat het systeem ongeveer 10 scherven zou moeten ondersteunen. We kunnen geen architecturale beslissingen nemen die deze waarde expliciet beperken.
  • We hebben een cloud, maar we gaan ervan uit dat een persoon nog steeds de mogelijkheid moet hebben wanneer hij binaire bestanden downloadt, op zijn laptop draait en alles prima werkt.
  • Wij gaan uit van iets wat Research zelden aanneemt: externe klanten kunnen doen wat ze willen. MongoDB is opensource. Dienovereenkomstig kunnen cliënten zo slim en boos zijn dat ze alles kapot willen maken. We speculeren dat Byzantijnse Feilors mogelijk afkomstig zijn.
  • Voor externe clients die zich buiten de perimeter bevinden, is er een belangrijke beperking: als deze functie is uitgeschakeld, mag er geen prestatieverlies worden waargenomen.
  • Een ander punt is over het algemeen anti-academisch: de compatibiliteit van eerdere en toekomstige versies. Oude stuurprogramma's moeten nieuwe updates ondersteunen, en de database moet oude stuurprogramma's ondersteunen.

Over het algemeen brengt dit allemaal beperkingen met zich mee.

Causale consistentiecomponenten

Ik zal het nu hebben over enkele componenten. Als we causale consistentie in het algemeen beschouwen, kunnen we blokken selecteren. We hebben gekozen uit werken die tot een bepaald blok behoren: Dependency Tracking, het kiezen van klokken, hoe deze klokken met elkaar kunnen worden gesynchroniseerd en hoe we de veiligheid garanderen - dit is een ruwe schets van waar ik het over zal hebben:

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Volledige afhankelijkheidstracking

Waarom is het nodig? Zodat wanneer gegevens worden gerepliceerd, elke record en elke gegevenswijziging informatie bevat over de veranderingen waarvan deze afhankelijk zijn. De allereerste en naïeve verandering is wanneer elk bericht dat een record bevat, informatie bevat over eerdere berichten:

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

In dit voorbeeld zijn het getal tussen accolades de recordnummers. Soms worden deze records met waarden zelfs in hun geheel overgedragen, soms worden er enkele versies overgedragen. Het komt erop neer dat elke wijziging informatie bevat over de vorige (dit draagt ​​uiteraard alles in zich).

Waarom hebben we besloten deze aanpak (volledige tracking) niet te gebruiken? Uiteraard omdat deze aanpak onpraktisch is: elke verandering aan een sociaal netwerk hangt af van alle eerdere wijzigingen aan dat sociale netwerk, waarbij bijvoorbeeld Facebook of VKontakte bij elke update worden overgedragen. Niettemin is er veel onderzoek gedaan naar Full Dependency Tracking – dit zijn pre-sociale netwerken; in sommige situaties werkt het echt.

Expliciete afhankelijkheidstracking

De volgende is beperkter. Hierbij wordt ook gekeken naar de overdracht van informatie, maar alleen datgene wat er duidelijk afhankelijk van is. Wat ervan afhangt, wordt in de regel bepaald door de Applicatie. Wanneer gegevens worden gerepliceerd, retourneert de query alleen antwoorden als aan eerdere afhankelijkheden is voldaan, dat wil zeggen, wordt weergegeven. Dit is de essentie van hoe causale consistentie werkt.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Ze ziet dat record 5 afhankelijk is van records 1, 2, 3, 4 - dienovereenkomstig wacht ze voordat de cliënt toegang heeft tot de wijzigingen die zijn aangebracht door Penny's toegangsbeslissing, terwijl alle eerdere wijzigingen al door de database zijn gegaan.

Dat past ook niet bij ons, want er is nog steeds te veel informatie en dat vertraagt. Er is een andere aanpak...

Lamport-klok

Ze zijn erg oud. Lamport Clock betekent dat deze afhankelijkheden worden samengevoegd tot een scalaire functie, die Lamport Clock wordt genoemd.

Een scalaire functie is een abstract getal. Het wordt vaak logische tijd genoemd. Bij elke gebeurtenis wordt deze teller verhoogd. Counter, die momenteel bekend is bij het proces, verzendt elk bericht. Het is duidelijk dat processen kunnen worden gedesynchroniseerd, ze kunnen totaal verschillende tijden hebben. Niettemin houdt het systeem op de een of andere manier de klok in evenwicht met dergelijke berichten. Wat gebeurt er in dit geval?

Ik heb die grote scherf in tweeën gedeeld om het duidelijk te maken: vrienden kunnen in één knooppunt wonen, dat een deel van de verzameling bevat, en Feed kan in een ander knooppunt wonen, dat een deel van deze verzameling bevat. Is het duidelijk hoe ze uit de pas kunnen lopen? De eerste feed zegt: "Gerepliceerd", en dan Vrienden. Als het systeem geen enkele garantie biedt dat de feed niet wordt weergegeven totdat de Friends-afhankelijkheden in de Friends-collectie ook zijn geleverd, dan hebben we precies de situatie die ik noemde.

Je ziet hoe de tellertijd op Feed logischerwijs toeneemt:

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

De belangrijkste eigenschap van deze Lamport Clock en Causale consistentie (uitgelegd via Lamport Clock) is dus deze: als we gebeurtenissen A en B hebben, en gebeurtenis B hangt af van gebeurtenis A*, dan volgt hieruit dat de logische tijd van gebeurtenis A kleiner is dan Logische tijd van gebeurtenis B.

* Soms zeggen ze ook dat A vóór B gebeurde, dat wil zeggen dat A vóór B gebeurde - dit is een bepaalde relatie die gedeeltelijk de hele reeks gebeurtenissen ordent die in het algemeen plaatsvonden.

Het tegendeel is onjuist. Dit is eigenlijk een van de belangrijkste nadelen van Lamport Clock: gedeeltelijke bestelling. Er bestaat een concept over gelijktijdige gebeurtenissen, dat wil zeggen gebeurtenissen waarbij noch (A gebeurde vóór B), noch (A gebeurde vóór B). Een voorbeeld zou Leonard's parallelle toevoeging zijn van iemand anders als vriend (niet eens Leonard, maar Sheldon bijvoorbeeld).
Dit is de eigenschap die vaak gebruikt wordt bij het werken met Lamport-klokken: ze kijken specifiek naar de functie en concluderen hieruit dat deze gebeurtenissen misschien afhankelijk zijn. Omdat één manier waar is: als LogicalTime A kleiner is dan LogicalTime B, dan kan B niet vóór A plaatsvinden; en als het meer is, dan misschien.

Vectorklok

De logische ontwikkeling van de Lamport-klok is de Vectorklok. Ze verschillen doordat elk knooppunt hier zijn eigen afzonderlijke klok bevat, en ze worden verzonden als een vector.
In dit geval zie je dat de nulde index van de vector verantwoordelijk is voor Feed, en de eerste index van de vector voor Friends (elk van deze knooppunten). En nu zullen ze toenemen: de nulindex van “Feed” neemt toe bij het schrijven – 1, 2, 3:

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Waarom is Vectorklok beter? Omdat je hiermee kunt achterhalen welke gebeurtenissen gelijktijdig plaatsvinden en wanneer ze op verschillende knooppunten plaatsvinden. Dit is erg belangrijk voor een shardingsysteem als MongoDB. Wij hebben hier echter niet voor gekozen, ook al is het een prachtig ding, het werkt prima en het zou waarschijnlijk bij ons passen...

Als we 10 scherven hebben, kunnen we geen 10 componenten overbrengen, zelfs niet als we deze comprimeren of iets anders bedenken - de lading zal nog steeds meerdere keren kleiner zijn dan het volume van deze hele vector. Daarom hebben we, met hart en tanden op elkaar geklemd, deze aanpak verlaten en zijn we overgegaan op een andere.

Spanner TrueTime. Atoomklok

Ik zei dat er een verhaal over Spanner zou zijn. Dit is iets cools, rechtstreeks uit de XNUMXe eeuw: atoomklokken, GPS-synchronisatie.

Wat is het idee? “Spanner” is een Google-systeem dat sinds kort zelfs beschikbaar is voor mensen (ze hebben er SQL aan toegevoegd). Elke transactie daar heeft een tijdstempel. Omdat de tijd gesynchroniseerd is*, kan aan elke gebeurtenis een specifieke tijd worden toegewezen - atoomklokken hebben een wachttijd, waarna er gegarandeerd een andere tijd “gebeurt”.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Door simpelweg naar de database te schrijven en enige tijd te wachten, wordt de serialiseerbaarheid van de gebeurtenis dus automatisch gegarandeerd. Ze hebben het sterkste consistentiemodel dat je in principe kunt bedenken: het is externe consistentie.

* Dit is het grootste probleem met Lampart-klokken: ze zijn nooit synchroon op gedistribueerde systemen. Ze kunnen uiteenlopen; zelfs met NTP werken ze nog steeds niet zo goed. "Spanner" heeft een atoomklok en de synchronisatie lijkt microseconden te zijn.

Waarom hebben wij niet gekozen? We gaan er niet van uit dat onze gebruikers een ingebouwde atoomklok hebben. Als ze verschijnen, ingebouwd in elke laptop, zal er een soort supercoole GPS-synchronisatie zijn - dan ja... Maar voor nu is het beste dat mogelijk is Amazon, basisstations - voor fanatici... Dus gebruikten we andere horloges .

Hybride klok

Dit is eigenlijk wat in MongoDB werkt als het gaat om het garanderen van causale consistentie. Hoe zijn ze hybride? Hybride is een scalaire waarde, maar bestaat uit twee componenten:

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

  • De eerste is het Unix-tijdperk (hoeveel seconden zijn er verstreken sinds het “begin van de computerwereld”).
  • De tweede is een verhoging, ook een 32-bits niet-ondertekende int.

Dat is eigenlijk alles. Er is deze aanpak: het deel dat verantwoordelijk is voor de tijd wordt voortdurend gesynchroniseerd met de klok; elke keer dat er een update plaatsvindt, wordt dit deel gesynchroniseerd met de klok en blijkt dat de tijd altijd min of meer correct is, en door te verhogen kun je onderscheid maken tussen gebeurtenissen die op hetzelfde tijdstip plaatsvonden.

Waarom is dit belangrijk voor MongoDB? Omdat je hiermee op een bepaald moment een soort back-uprestaurant kunt maken, dat wil zeggen dat de gebeurtenis op tijd wordt geïndexeerd. Dit is belangrijk wanneer bepaalde evenementen nodig zijn; Voor een database zijn gebeurtenissen veranderingen in de database die met bepaalde tijdsintervallen hebben plaatsgevonden.

Ik zal je de belangrijkste reden alleen aan jou vertellen (vertel het alsjeblieft aan niemand)! We hebben dit gedaan omdat georganiseerde, geïndexeerde gegevens er zo uitzien in MongoDB OpLog. OpLog is een datastructuur die absoluut alle wijzigingen in de database bevat: ze gaan eerst naar OpLog en worden vervolgens toegepast op Storage zelf in het geval dat het een gerepliceerde datum of scherf is.

Dit was de belangrijkste reden. Toch zijn er ook praktische vereisten voor het ontwikkelen van een database, wat betekent dat deze eenvoudig moet zijn: weinig code, zo min mogelijk kapotte dingen die herschreven en getest moeten worden. Het feit dat onze oplogs werden geïndexeerd door hybride klokken hielp enorm en stelde ons in staat de juiste keuze te maken. Het heeft echt zijn vruchten afgeworpen en heeft op de een of andere manier op magische wijze gewerkt aan het allereerste prototype. Het was erg gaaf!

Kloksynchronisatie

Er zijn verschillende synchronisatiemethoden beschreven in de wetenschappelijke literatuur. Ik heb het over synchronisatie als we twee verschillende scherven hebben. Als er één replicaset is, is er geen synchronisatie nodig: dit is een “single master”; we hebben een OpLog, waarin alle veranderingen vallen - in dit geval is alles al opeenvolgend geordend in de "Oplog" zelf. Maar als we twee verschillende scherven hebben, is tijdsynchronisatie hier belangrijk. Dit is waar de vectorklok meer hielp! Maar wij hebben ze niet.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

De tweede is geschikt - dit is "Heartbeats". Het is mogelijk om bepaalde signalen uit te wisselen die elke tijdseenheid voorkomen. Maar Heartbeats zijn te langzaam, we kunnen onze klant geen latentie bieden.

Echte tijd is natuurlijk iets wonderbaarlijks. Maar nogmaals, dit is waarschijnlijk de toekomst... Hoewel het al mogelijk is in Atlas, bestaan ​​er al snelle “Amazon”-tijdsynchronisatoren. Maar het zal niet voor iedereen beschikbaar zijn.

Roddelen is wanneer alle berichten tijd bevatten. Dit is ongeveer wat wij gebruiken. Elk bericht tussen knooppunten, een stuurprogramma, een dataknooppuntrouter, werkelijk alles voor MongoDB is een soort element, een databasecomponent die een klok bevat die loopt. Ze hebben overal de betekenis van hybride tijd, het wordt overgedragen. 64 bits? Dit maakt het mogelijk, dit is mogelijk.

Hoe werkt het allemaal samen?

Hier kijk ik naar een replicaset om het wat makkelijker te maken. Er zijn primaire en secundaire. Secundair voert replicatie uit en is niet altijd volledig gesynchroniseerd met Primair.

Er vindt een invoeging plaats in de “Primery” met een bepaalde tijdswaarde. Deze insert verhoogt de interne telling met 11, als dit het maximum is. Of het controleert de klokwaarden en synchroniseert met de klok als de klokwaarden groter zijn. Hierdoor kunt u op tijd ordenen.

Nadat hij de opname heeft gemaakt, doet zich een belangrijk moment voor. De klok bevindt zich in "MongoDB" en wordt alleen verhoogd als er naar "Oplog" wordt geschreven. Dit is de gebeurtenis die de toestand van het systeem verandert. In absoluut alle klassieke artikelen wordt van een gebeurtenis gesproken wanneer een bericht een knooppunt bereikt: het bericht is aangekomen, wat betekent dat het systeem van status is veranderd.

Dit komt doordat tijdens het onderzoek niet geheel duidelijk is hoe deze boodschap geïnterpreteerd zal worden. We weten zeker dat als het niet wordt weerspiegeld in de "Oplog", het op geen enkele manier zal worden geïnterpreteerd en dat een verandering in de status van het systeem slechts een vermelding in de "Oplog" is. Dit vereenvoudigt alles voor ons: het model vereenvoudigt het en stelt ons in staat het binnen één replicaset te organiseren, en vele andere nuttige dingen.

De waarde die al naar de "Oplog" is geschreven, wordt geretourneerd - we weten dat de "Oplog" deze waarde al bevat en dat de tijd 12 is. Nu begint het lezen bijvoorbeeld vanaf een ander knooppunt (Secundair) en verzendt het na ClusterTime in de boodschap. Hij zegt: “Ik heb alles nodig wat er op zijn minst na twaalf uur of tijdens twaalf uur is gebeurd” (zie afbeelding hierboven).

Dit wordt Causaal een consistente (CAT) genoemd. In theorie bestaat er zo'n concept dat dit een stukje tijd is, wat op zichzelf consistent is. In dit geval kunnen we zeggen dat dit de toestand van het systeem is die op tijdstip 12 werd waargenomen.

Nu is er hier nog niets, omdat dit de situatie simuleert waarin je de secundaire nodig hebt om gegevens uit de primaire te repliceren. Hij wacht... En nu de gegevens zijn gearriveerd, geeft hij deze waarden terug.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Dat is ongeveer hoe het allemaal werkt. Bijna.

Wat betekent ‘bijna’? Laten we aannemen dat er iemand is die heeft gelezen en begrepen hoe dit allemaal werkt. Ik realiseerde me dat elke keer dat ClusterTime plaatsvindt, de interne logische klok wordt bijgewerkt, en dat de volgende invoer met één wordt verhoogd. Deze functie duurt 20 regels. Laten we zeggen dat deze persoon het grootste 64-bits getal verzendt, min één.

Waarom "min één"? Omdat de interne klok in deze waarde wordt vervangen (dit is uiteraard de grootst mogelijke en groter dan de huidige tijd), zal er een invoer plaatsvinden in "Oplog" en zal de klok worden verhoogd met een andere eenheid - en er zal al een maximale waarde zijn (er zijn eenvoudigweg alle eenheden, je kunt nergens anders heen), onheilige ints).

Het is duidelijk dat het systeem hierna absoluut ontoegankelijk wordt voor wat dan ook. Het kan alleen worden gelost en schoongemaakt - veel handwerk. Volledige beschikbaarheid:

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Bovendien, als dit ergens anders wordt herhaald, valt het hele cluster eenvoudigweg omver. Een absoluut onaanvaardbare situatie die iedereen heel snel en gemakkelijk kan organiseren! Daarom beschouwden we dit moment als een van de belangrijkste. Hoe het te voorkomen?

Onze manier is om clusterTime te ondertekenen

Dit is hoe het wordt verzonden in het bericht (vóór de blauwe tekst). Maar we zijn ook begonnen met het genereren van een handtekening (blauwe tekst):

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

De handtekening wordt gegenereerd door een sleutel die in de database wordt opgeslagen, binnen een beveiligde omgeving; zelf wordt gegenereerd en bijgewerkt (gebruikers zien er niets van). Er wordt een hash gegenereerd en elk bericht wordt ondertekend bij het maken en gevalideerd bij ontvangst.
De vraag rijst waarschijnlijk in de hoofden van mensen: “In hoeverre vertraagt ​​dit de zaken?” Ik zei je dat het snel zou moeten werken, vooral als deze functie ontbreekt.

Wat betekent het in dit geval om causale consistentie te gebruiken? Dit is om de parameter afterClusterTime weer te geven. Zonder dit zal het toch gewoon waarden doorgeven. Roddelen werkt vanaf versie 3.6 altijd.

Als we het constante genereren van handtekeningen achterwege laten, zal het systeem vertragen, zelfs als er geen functie is die niet aan onze benaderingen en eisen voldoet. Dus wat hebben we gedaan?

Doe het snel!

Het is vrij eenvoudig, maar de truc is interessant – ik zal het delen, misschien is iemand geïnteresseerd.
We hebben een hash waarin de ondertekende gegevens worden opgeslagen. Alle gegevens gaan via de cache. De cache tekent niet de specifieke tijd, maar de Range. Wanneer er een waarde binnenkomt, genereren we een bereik, maskeren we de laatste 16 bits en ondertekenen we deze waarde:

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Door zo’n handtekening te ontvangen, versnellen we het systeem (relatief) 65 duizend keer. Het werkt prima: toen we experimenten uitvoerden, nam de tijd bij een opeenvolgende update zelfs tienduizend keer af. Het is duidelijk dat dit niet werkt als ze het oneens zijn. Maar in de meeste praktische gevallen werkt het. De combinatie van de Range-handtekening en de handtekening loste het beveiligingsprobleem op.

Wat hebben we geleerd?

Lessen die we hieruit hebben geleerd:

  • We moeten materialen, verhalen en artikelen lezen, omdat we veel interessante dingen hebben. Wanneer we aan een bepaalde functie werken (vooral nu, wanneer we transacties deden, enz.), moeten we lezen en begrijpen. Het kost tijd, maar het is juist heel nuttig omdat het duidelijk maakt waar we staan. Het leek erop dat we niets nieuws hadden bedacht – we namen alleen de ingrediënten.

    Over het algemeen is er een bepaald verschil in denken als er een academische conferentie is (bijvoorbeeld Sigmon) - iedereen concentreert zich op nieuwe ideeën. Wat is er nieuw aan ons algoritme? Er is hier niets bijzonders nieuws. De nieuwigheid ligt eerder in de manier waarop we bestaande benaderingen met elkaar vermengden. Daarom is het eerste wat je moet doen de klassiekers lezen, te beginnen met Lampart.

  • In de productie zijn de eisen compleet anders. Ik ben er zeker van dat velen van jullie niet te maken hebben met ‘sferische’ databases in een abstract vacuüm, maar met normale, echte dingen die problemen hebben met beschikbaarheid, latentie en fouttolerantie.
  • Het laatste is dat we naar verschillende ideeën moesten kijken en verschillende totaal verschillende artikelen samen in één aanpak moesten combineren. Het idee over ondertekening kwam bijvoorbeeld meestal uit een artikel waarin het Paxos-protocol werd behandeld, dat voor niet-Byzantijnse failors binnen het autorisatieprotocol valt, voor Byzantijnse mislukkingen - buiten het autorisatieprotocol... Over het algemeen is dit precies wat we doen. uiteindelijk gedaan.

    Er is hier absoluut niets nieuws! Maar zodra we het allemaal door elkaar hebben gemengd... Het is hetzelfde als zeggen dat het recept voor de Olivier-salade onzin is, omdat eieren, mayonaise en komkommers al zijn uitgevonden... Het gaat over hetzelfde verhaal.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Ik zal hiermee eindigen. Bedankt!

vragen

Vraag uit het publiek (hierna B): – Bedankt, Michail, voor het rapport! Het onderwerp tijd is interessant. Je maakt gebruik van Roddelen. Ze zeiden dat iedereen zijn eigen tijd heeft, iedereen kent zijn lokale tijd. Zoals ik het begrijp hebben we een driver - er kunnen veel klanten zijn met drivers, ook queryplanners, ook shards... En waar komt het systeem op neer als we plotseling een discrepantie hebben: iemand besluit dat het voor een minuut voorsprong, iemand een minuut achterstand? Waar zullen we eindigen?

MT: – Geweldige vraag inderdaad! Ik wilde het alleen over scherven hebben. Als ik de vraag goed begrijp, hebben we de volgende situatie: er is scherf 1 en scherf 2, het lezen gebeurt vanuit deze twee scherven - ze hebben een discrepantie, ze hebben geen interactie met elkaar, omdat de tijd die ze weten anders is, vooral de tijd dat ze in oplogs voorkomen.
Laten we zeggen dat shard 1 een miljoen records heeft gemaakt, shard 2 helemaal niets heeft gedaan en dat het verzoek uitkwam op twee scherven. En de eerste heeft een afterClusterTime van meer dan een miljoen. In een dergelijke situatie zal Shard 2, zoals ik heb uitgelegd, helemaal nooit reageren.

В: – Ik wilde weten hoe ze synchroniseren en één logische tijd kiezen?

MT: - Zeer eenvoudig te synchroniseren. Shard, wanneer afterClusterTime naar hem toe komt en hij geen tijd vindt in de "Oplog", initieert geen goedkeuring. Dat wil zeggen, hij verhoogt zijn tijd met zijn handen naar deze waarde. Dit betekent dat er geen evenementen zijn die aan dit verzoek voldoen. Hij creëert deze gebeurtenis kunstmatig en wordt zo Causaal Consistent.

В: – Wat als er hierna nog andere gebeurtenissen naar hem toekomen die ergens in het netwerk verloren zijn gegaan?

MT: – Shard is zo ontworpen dat ze niet meer terugkomen, aangezien het een enkele master is. Als hij zich al heeft aangemeld, komen ze niet, maar komen later. Het kan niet gebeuren dat er ergens iets blijft hangen, dan schrijft hij niet, en dan komen deze gebeurtenissen - en wordt de causale consistentie verbroken. Als hij niet schrijft, moeten ze allemaal als volgende komen (hij zal op ze wachten).

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

В: – Ik heb een aantal vragen over de wachtrijen. Causale consistentie gaat ervan uit dat er een specifieke reeks acties is die moeten worden uitgevoerd. Wat gebeurt er als een van onze pakketten verdwijnt? Hier komt de 10e, 11e... de 12e is verdwenen en alle anderen wachten tot deze uitkomt. En plotseling stierf onze auto, we kunnen niets doen. Is er een maximale lengte van de wachtrij die zich ophoopt voordat deze wordt uitgevoerd? Welke fatale mislukking treedt op als een bepaalde toestand verloren gaat? Bovendien, als we opschrijven dat er een eerdere toestand is, moeten we daar dan op de een of andere manier vanuit gaan? Maar ze duwden hem niet weg!

MT: – Ook een goede vraag! Wat doen we? MongoDB heeft het concept van quorum schrijft, quorum leest. In welke gevallen kan een bericht verloren gaan? Wanneer een schrijfbewerking geen quorum is of wanneer een leesbewerking geen quorum is (er kan ook een soort rommel blijven hangen).
Met betrekking tot causale consistentie werd een grote experimentele test uitgevoerd, met als resultaat dat in het geval dat schrijf- en leesbewerkingen niet-quorum zijn, er schendingen van de causale consistentie optreden. Precies wat je zegt!

Ons advies: gebruik ten minste quorumlezen bij gebruik van causale consistentie. In dit geval gaat er niets verloren, zelfs als het quorumrecord verloren gaat... Dit is een orthogonale situatie: als de gebruiker niet wil dat gegevens verloren gaan, moet hij een quorumrecord gebruiken. Causale consistentie garandeert geen duurzaamheid. De duurzaamheid wordt gegarandeerd door replicatie en de machines die daarmee gepaard gaan.

В: – Wanneer we een instance creëren die sharding voor ons uitvoert (respectievelijk niet master, maar slave), vertrouwt deze op de Unix-tijd van zijn eigen machine of op de tijd van de “master”; Synchroniseert het voor de eerste keer of periodiek?

MT: – Ik zal het nu verduidelijken. Shard (d.w.z. horizontale partitie) – er is altijd een Primary daar. En een scherf kan een ‘master’ hebben en er kunnen replica’s zijn. Maar de scherf ondersteunt altijd opname, omdat deze een bepaald domein moet ondersteunen (de scherf heeft primair).

В: – Dus alles hangt puur af van de “meester”? Wordt er altijd gebruik gemaakt van mastertijd?

MT: - Ja. Je kunt figuurlijk zeggen: de klok tikt als er een vermelding in de “master”, in de “Oplog” plaatsvindt.

В: – Wij een klant hebben die verbinding maakt en niets van de tijd hoeft te weten?

MT: – Je hoeft helemaal niets te weten! Als we het hebben over hoe het bij de cliënt werkt: wanneer de cliënt Causale consistentie wil gebruiken, moet hij een sessie openen. Nu is alles aanwezig: transacties in de sessie, en rechten ophalen... Een sessie is de volgorde van logische gebeurtenissen die plaatsvinden bij de client.

Als hij deze sessie opent en daar zegt dat hij Causale consistentie wil (als de sessie standaard Causale consistentie ondersteunt), werkt alles automatisch. De chauffeur onthoudt deze tijd en verhoogt deze wanneer hij een nieuw bericht ontvangt. Het onthoudt welk antwoord de vorige heeft geretourneerd van de server die de gegevens heeft geretourneerd. Het volgende verzoek bevat afterCluster("time groter dan dit").

De klant hoeft helemaal niets te weten! Dit is voor hem volkomen ondoorzichtig. Als mensen deze functies gebruiken, wat kunnen ze dan doen? Ten eerste kunt u veilig secundaire bestanden lezen: u kunt naar Primary schrijven en lezen van geografisch gerepliceerde secundaire bestanden en er zeker van zijn dat het werkt. Tegelijkertijd kunnen sessies die zijn opgenomen op Primary zelfs worden overgedragen naar Secondary, d.w.z. u kunt niet één sessie gebruiken, maar meerdere.

В: – Een nieuwe laag van computerwetenschappen – CRDT-gegevenstypen (Conflict-free Replicated Data Types) – is sterk gerelateerd aan het onderwerp van uiteindelijke consistentie. Heeft u erover nagedacht om dit soort gegevens in de database te integreren en wat kunt u daarover zeggen?

MT: - Goede vraag! CRDT is zinvol voor schrijfconflicten: in MongoDB, enkele master.

В: – Ik heb een vraag van de devops. In de echte wereld zijn er zulke jezuïtische situaties waarin de Byzantijnse mislukking plaatsvindt en slechte mensen binnen de beschermde perimeter het protocol beginnen binnen te dringen en op een speciale manier ambachtelijke pakketten sturen?

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

MT: – Slechte mensen binnen de perimeter zijn als een Trojaans paard! Slechte mensen binnen de perimeter kunnen veel slechte dingen doen.

В: – Het is duidelijk dat je grofweg een gat in de server laat waardoor je een dierentuin met olifanten kunt plaatsen en het hele cluster voor altijd kunt laten instorten... Het zal tijd kosten voor handmatig herstel... Dit is, op zijn zachtst gezegd, fout. Aan de andere kant is dit interessant: zijn er in het echte leven, in de praktijk, situaties waarin van nature soortgelijke interne aanvallen plaatsvinden?

MT: – Omdat ik in het echte leven zelden beveiligingsinbreuken tegenkom, kan ik niet zeggen of ze zich voordoen. Maar als we het hebben over de ontwikkelingsfilosofie, denken we zo: we hebben een perimeter die de jongens biedt die voor veiligheid zorgen - dit is een kasteel, een muur; en binnen de perimeter kun je doen wat je wilt. Het is duidelijk dat er gebruikers zijn met de mogelijkheid om alleen te bekijken, en er zijn gebruikers met de mogelijkheid om de directory te wissen.

Afhankelijk van de rechten kan de schade die gebruikers kunnen aanrichten een muis zijn, of een olifant. Het is duidelijk dat een gebruiker met volledige rechten überhaupt alles kan doen. Een gebruiker met beperkte rechten kan aanzienlijk minder schade aanrichten. In het bijzonder kan het het systeem niet kapot maken.

В: – In de beschermde perimeter heeft iemand geprobeerd onverwachte protocollen voor de server te maken om de server volledig te vernietigen, en als je geluk hebt, het hele cluster... Wordt het ooit zo “goed”?

MT: “Ik heb nog nooit van zulke dingen gehoord.” Dat je op deze manier een server kunt laten crashen is geen geheim. Van binnen falen, omdat je van het protocol bent, een geautoriseerde gebruiker bent die zoiets in het bericht kan schrijven... In feite is het onmogelijk, omdat het nog steeds wordt geverifieerd. Het is mogelijk om deze authenticatie uit te schakelen voor gebruikers die dit niet willen - dan is dat hun probleem; ze hebben grofweg de muren zelf vernield en je kunt er een olifant in duwen, die zal vertrappen... Maar over het algemeen kun je je verkleden als reparateur, komen en hem eruit trekken!

В: – Bedankt voor het rapport. Sergej (Yandex). Er is een constante in Mong die het aantal stemgerechtigde leden in de replicaset beperkt, en deze constante is 7 (zeven). Waarom is dit een constante? Waarom is dit niet een soort parameter?

MT: – We hebben replicasets met 40 knooppunten. Er is altijd een meerderheid. Ik weet niet welke versie...

В: – In Replica Set kun je niet-stemgerechtigde leden hebben, maar er zijn maximaal 7 stemgerechtigde leden. Hoe kunnen we in dit geval de sluiting overleven als onze Replica Set is verspreid over 3 datacenters? Eén datacenter kan gemakkelijk worden uitgeschakeld en een andere machine kan uitvallen.

MT: – (EN) Dit valt al een beetje buiten de reikwijdte van het rapport. Dit is een algemene vraag. Misschien kan ik je er later over vertellen.

HighLoad++, Mikhail Tyulenev (MongoDB): Causale consistentie: van theorie naar praktijk

Sommige advertenties 🙂

Bedankt dat je bij ons bent gebleven. Vind je onze artikelen leuk? Wil je meer interessante inhoud zien? Steun ons door een bestelling te plaatsen of door vrienden aan te bevelen, cloud VPS voor ontwikkelaars vanaf $ 4.99, een unieke analoog van servers op instapniveau, die door ons voor u is uitgevonden: De hele waarheid over VPS (KVM) E5-2697 v3 (6 kernen) 10 GB DDR4 480 GB SSD 1 Gbps vanaf $ 19 of hoe een server te delen? (beschikbaar met RAID1 en RAID10, tot 24 cores en tot 40GB DDR4).

Dell R730xd 2x goedkoper in Equinix Tier IV datacenter in Amsterdam? Alleen hier 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV vanaf $199 in Nederland! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - vanaf $99! Lees over Hoe infrastructuur corp te bouwen. klasse met het gebruik van Dell R730xd E5-2650 v4-servers ter waarde van 9000 euro voor een cent?

Bron: www.habr.com

Voeg een reactie