Waarom heeft u semi-synchrone replicatie nodig?

Dag Allemaal. Vladislav Rodin heeft contact opgenomen. Momenteel geef ik cursussen over Software Architectuur en High-Stress Software Architectuur bij OTUS. In afwachting van de start van een nieuwe cursusstroom "High Load-architect" Ik besloot een kort stukje origineel materiaal te schrijven dat ik met jullie wil delen.

Waarom heeft u semi-synchrone replicatie nodig?

Introductie

Vanwege het feit dat de HDD slechts ongeveer 400-700 bewerkingen per seconde kan uitvoeren (wat onvergelijkbaar is met typische rps op een systeem met hoge belasting), is de klassieke schijfdatabase het knelpunt van de architectuur. Daarom is het noodzakelijk om speciale aandacht te besteden aan de schaalpatronen van deze opslag.

Momenteel zijn er twee databaseschaalpatronen: replicatie en sharding. Met Sharding kunt u de schrijfbewerking schalen en als gevolg daarvan de RPS per schrijfbewerking per server in uw cluster verlagen. Met replicatie kunt u hetzelfde doen, maar dan met leesbewerkingen. Het is dit patroon waaraan dit artikel is gewijd.

Replicatie

Als je replicatie op een heel hoog niveau bekijkt, is het eenvoudig: je had één server, daar stonden gegevens op, en toen kon deze server de last van het lezen van deze gegevens niet meer aan. U voegt nog een paar servers toe, synchroniseert gegevens tussen alle servers en de gebruiker kan vanaf elke server in uw cluster lezen.

Ondanks de schijnbare eenvoud zijn er verschillende opties om verschillende implementaties van dit schema te classificeren:

  • Op rollen in het cluster (master-master of master-slave)
  • Door verzonden objecten (rijgebaseerd, statementgebaseerd of gemengd)
  • Volgens het knooppuntsynchronisatiemechanisme

Vandaag behandelen we punt 3.

Hoe komt een transactiecommit tot stand?

Dit onderwerp houdt niet direct verband met replicatie; er kan een apart artikel over worden geschreven, maar aangezien verder lezen nutteloos is zonder het mechanisme voor het vastleggen van transacties te begrijpen, wil ik u aan de meest fundamentele zaken herinneren. Een transactiecommit vindt plaats in 3 fasen:

  1. Een transactie vastleggen in het databaselogboek.
  2. Een transactie gebruiken in een database-engine.
  3. Het retourneren van een bevestiging aan de klant dat de transactie succesvol is toegepast.

In verschillende databases kan dit algoritme nuances hebben: in de InnoDB-engine van de MySQL-database zijn er bijvoorbeeld twee logboeken: één voor replicatie (binair log) en de andere voor het onderhouden van ACID (logboek ongedaan maken/opnieuw uitvoeren), terwijl in PostgreSQL er is één log die beide functies vervult (write ahead log = WAL). Maar wat hierboven wordt gepresenteerd, is precies het algemene concept, waardoor met dergelijke nuances geen rekening kan worden gehouden.

Synchrone (synchronisatie) replicatie

Laten we logica toevoegen om de ontvangen wijzigingen in het transactie-vastleggingsalgoritme te repliceren:

  1. Een transactie vastleggen in het databaselogboek.
  2. Een transactie gebruiken in een database-engine.
  3. Gegevens verzenden naar alle replica's.
  4. Bevestiging ontvangen van alle replica's dat er een transactie op is voltooid.
  5. Het retourneren van een bevestiging aan de klant dat de transactie succesvol is toegepast.

Met deze aanpak krijgen we een aantal nadelen:

  • de client wacht tot de wijzigingen op alle replica's zijn toegepast.
  • naarmate het aantal knooppunten in het cluster toeneemt, verkleinen we de kans dat de schrijfbewerking succesvol zal zijn.

Als alles min of meer duidelijk is met het eerste punt, dan zijn de redenen voor het tweede punt de moeite waard om uit te leggen. Als we tijdens synchrone replicatie geen antwoord ontvangen van ten minste één knooppunt, draaien we de transactie terug. Door het aantal knooppunten in het cluster te vergroten, vergroot u dus de kans dat een schrijfbewerking mislukt.

Kunnen we wachten op bevestiging van slechts een bepaald percentage knooppunten, bijvoorbeeld van 51% (quorum)? Ja dat kan, maar in de klassieke versie is bevestiging van alle knooppunten vereist, omdat we op deze manier volledige gegevensconsistentie in het cluster kunnen garanderen, wat een onbetwist voordeel is van dit soort replicatie.

Asynchrone (asynchrone) replicatie

Laten we het vorige algoritme aanpassen. We zullen ‘enige tijd later’ gegevens naar de replica’s sturen en ‘enige tijd later’ zullen de wijzigingen op de replica’s worden toegepast:

  1. Een transactie vastleggen in het databaselogboek.
  2. Een transactie gebruiken in een database-engine.
  3. Het retourneren van een bevestiging aan de klant dat de transactie succesvol is toegepast.
  4. Gegevens naar replica's verzenden en wijzigingen daarop toepassen.

Deze aanpak leidt ertoe dat het cluster snel werkt, omdat we de klant niet laten wachten tot de gegevens de replica's bereiken en zelfs worden vastgelegd.

Maar de voorwaarde dat gegevens “enige tijd later” op replica's worden gedumpt, kan leiden tot het verlies van een transactie en tot het verlies van een transactie die door de gebruiker is bevestigd, omdat als de gegevens geen tijd hadden om te worden gerepliceerd, er een bevestiging aan de klant zou komen. over het succes van de operatie is verzonden en het knooppunt waarop de wijzigingen zijn aangekomen, is gecrasht op de HDD, we verliezen de transactie, wat tot zeer onaangename gevolgen kan leiden.

Semi-gesynchroniseerde replicatie

Ten slotte komen we bij semi-synchrone replicatie. Dit type replicatie is niet erg bekend of gebruikelijk, maar is van groot belang omdat het de voordelen van zowel synchrone als asynchrone replicatie kan combineren.

Laten we proberen de twee voorgaande benaderingen te combineren. We houden de klant niet lang vast, maar we eisen wel dat de gegevens worden gerepliceerd:

  1. Een transactie vastleggen in het databaselogboek.
  2. Een transactie gebruiken in een database-engine.
  3. Gegevens naar replica's verzenden.
  4. Bevestiging ontvangen van de replica dat de wijzigingen zijn ontvangen (ze worden “enige tijd later” toegepast).
  5. Het retourneren van een bevestiging aan de klant dat de transactie succesvol is toegepast.

Houd er rekening mee dat bij dit algoritme transactieverlies alleen optreedt als zowel het knooppunt dat de wijzigingen ontvangt als het replicaknooppunt falen. De waarschijnlijkheid van een dergelijke mislukking wordt als laag beschouwd en deze risico's worden aanvaard.

Maar bij deze aanpak bestaat er een mogelijk risico op fantoomlezen. Laten we ons het volgende scenario voorstellen: in stap 4 hebben we van geen enkele replica een bevestiging ontvangen. We moeten deze transactie ongedaan maken en geen bevestiging terugsturen naar de klant. Omdat de gegevens in stap 2 zijn toegepast, is er een tijdsverschil tussen het einde van stap 2 en het terugdraaien van de transactie, waarin parallelle transacties wijzigingen kunnen zien die niet in de database zouden moeten voorkomen.

Verliesloze semi-synchrone replicatie

Als je een beetje nadenkt, kun je de stappen van het algoritme gewoon omkeren en het probleem van fantoomlezingen in dit scenario oplossen:

  1. Een transactie vastleggen in het databaselogboek.
  2. Replicagegevens verzenden.
  3. Bevestiging ontvangen van de replica dat de wijzigingen zijn ontvangen (ze worden “enige tijd later” toegepast).
  4. Een transactie gebruiken in een database-engine.
  5. Het retourneren van een bevestiging aan de klant dat de transactie succesvol is toegepast.

Nu voeren we alleen wijzigingen door als ze zijn gerepliceerd.

Uitgang

Zoals altijd zijn er geen ideale oplossingen; er is een reeks oplossingen, die elk hun eigen voor- en nadelen hebben en geschikt zijn voor het oplossen van verschillende soorten problemen. Dit geldt absoluut voor het kiezen van een mechanisme voor het synchroniseren van gegevens in een gerepliceerde database. De reeks voordelen die semi-synchrone replicatie heeft, is voldoende solide en interessant om de aandacht waard te zijn, ondanks de lage prevalentie ervan.

Dat is alles. Zie je bij cursus!

Bron: www.habr.com

Voeg een reactie