V: Eén serviceserver, één database, operatie: zoek het huidige saldo van de gebruiker op, trek 3% van het huidige saldo af als afhandelingsvergoeding
Gesynchroniseerd slot DB-slot
V: Twee serviceservers, één database, operatie: zoek het huidige saldo van de gebruiker op, trek 3% van het huidige saldo af als afhandelingsvergoeding Gedistribueerde sloten
Wat voor soort gedistribueerde vergrendeling hebben we nodig? Het kan ervoor zorgen dat in een gedistribueerde applicatiecluster dezelfde methode slechts door één thread op één machine tegelijk kan worden uitgevoerd.
Als deze sluis een reentrant lock is (vermijd deadlocks)
Dit slot is het beste een blokkeringsslot (overweeg of je dit wilt volgens de behoeften van je bedrijf).
Dit slot is het beste om een eerlijk slot te zijn (denk of je dit wilt of niet, afhankelijk van de zakelijke behoeften).
Er zijn zeer beschikbare acquistie- en release-lockfuncties
De prestaties van de acquisitie- en release-locks zijn beter
1. Gedistribueerde sloten gebaseerd op databases
Gedistribueerde locks gebaseerd op tabelgebaseerde implementaties
Wanneer we een methode willen vergrendelen, voer je de volgende SQL uit: Invoegen in methodLock(method_name,desc) waarden ('method_name','desc')
Omdat we een uniciteitsbeperking op de method_name hebben gemaakt, zal als meerdere verzoeken tegelijk aan de database worden gestuurd, de database ervoor zorgen dat slechts één operatie kan slagen, waarna we kunnen aannemen dat de thread die de methodevergrendeling succesvol heeft verkregen, de inhoud van de methodebody kan uitvoeren.
Wanneer de methode wordt uitgevoerd, moet je als je de lock wilt losmaken, de volgende SQL uitvoeren: verwijderen uit methodLock waar method_name ='method_name'
Deze eenvoudige implementatie hierboven heeft de volgende problemen:
- Deze vergrendeling hangt af van de beschikbaarheid van de database, die één punt is en ervoor zorgt dat het bedrijfssysteem onbeschikbaar wordt zodra de database wordt opgehangen.
- Deze vergrendeling heeft geen vervaltijd, en zodra de ontgrendelingsoperatie mislukt, blijft het vergrendelingsrecord in de database staan en kunnen andere threads de vergrendeling niet langer verkrijgen.
- Deze vergrendeling kan alleen niet-blokkerend zijn, omdat de invoegoperatie van data direct een foutmelding zal geven zodra de invoeging mislukt. Threads die geen locks verkrijgen, komen niet in de wachtrij en moeten de lock acquisitie-operatie opnieuw activeren om de lock opnieuw te verkrijgen.
- Het slot is niet-terugtrekkend en dezelfde schroefdraad kan het slot pas opnieuw krijgen als het wordt losgemaakt. Omdat de data in de data al bestaat.
- Dit slot is een oneerlijk slot, en alle draden die op het slot wachten concurreren door geluk om het slot.
Natuurlijk kunnen we bovenstaande problemen ook op andere manieren oplossen.
- Is de database een enkel punt? Bouw twee databases, en de data wordt in beide richtingen gesynchroniseerd. Zodra je hebt opgehangen, schakel dan snel over naar de back-upbibliotheek.
- Geen vervaltijd? Doe gewoon een geplande taak om de timeout-data in de database op regelmatige tijdstippen op te schonen.
- Niet-blokkerend? Doe een while-lus totdat de insert slaagt en dan het succes teruggeeft.
- Niet-reentrant? Voeg een veld toe aan de databasetabel om de hostinformatie en threadinformatie van de machine die momenteel de lock krijgt vast te leggen, en de volgende keer dat je de lock krijgt, vraag eerst de database op; als de hostinformatie en threadinformatie van de huidige machine in de database te vinden zijn, kun je de lock direct aan hem toewijzen.
- Oneerlijk? Maak een andere tussenliggende tabel aan om alle threads die op de lock wachten vast te leggen, en sorteer ze op basis van de aanmaaktijd, en alleen de eerste aangemaakte threads mag de lock verkrijgen
Gedistribueerde sloten gebaseerd op exclusieve sloten
Naast het toevoegen en verwijderen van records in de datatabel kunnen gedistribueerde sloten ook worden geïmplementeerd met behulp van de vergrendelingen die bij de data horen.
We gebruiken ook de databasetabel die we net hebben aangemaakt. Gedistribueerde sloten kunnen worden geïmplementeerd via exclusieve vergrendelingen op databases. De op MySQL gebaseerde InnoDB-engine kan de volgende methoden gebruiken om lock-up operaties te implementeren:
Als je na de query-instructie 'for update' toevoegt, zal de database tijdens het queryproces een exclusieve lock aan de databasetabel toevoegen. Wanneer een exclusieve vergrendeling aan een record wordt toegevoegd, kunnen andere threads geen exclusieve vergrendeling meer toevoegen aan het record op die lijn.
We kunnen denken dat de thread die de exclusieve lock verkrijgt, ook de gedistribueerde lock kan verkrijgen, en wanneer de lock is verkregen, kan de bedrijfslogica van de methode worden uitgevoerd en vervolgens worden ontgrendeld via de volgende methoden:
public void unlock(){ connection.commit(); }
via connection.commit(); operatie om het slot te openen.
Deze methode kan de hierboven genoemde problemen over het onvermogen om het slot los te maken en te blokkeren effectief oplossen.
Sloten blokkeren? De for update-instructie keert direct terug na succesvolle uitvoering en blijft geblokkeerd totdat deze slaagt.
Dienst uitgeschakeld na de vergrendeling, kan niet worden vrijgegeven? Op deze manier maakt de database de vergrendeling vanzelf los nadat de dienst uitvalt.
Het lost echter nog steeds het probleem van database single point, reentrancy en fair locking niet direct op.
Om samen te vatten hoe de database gebruikt wordt om gedistribueerde sloten te implementeren, die beide afhankelijk zijn van een tabel in de database, is het ene bepalen of er een slot bestaat door het bestaan van records in de tabel, en het andere is het implementeren van gedistribueerde sloten via de exclusieve vergrendeling van de database.
Voordelen van gedistribueerde vergrendeling in databases
Direct met behulp van de database is het gemakkelijk te begrijpen.
Nadelen van het implementeren van gedistribueerde locks in databases
Er zullen verschillende problemen zijn, en de hele oplossing zal steeds complexer worden tijdens het oplossen van het probleem.
Het bedienen van de database vereist bepaalde overhead, en prestatieproblemen moeten worden meegenomen.
2. Gedistribueerde locks gebaseerd op cache
In vergelijking met de databasegebaseerde gedistribueerde vergrendelingsoplossing zal de cache-gebaseerde implementatie beter presteren qua prestaties.
Er zijn momenteel veel volwassen cachingproducten, waaronder Redis, memcached, enzovoort. Hier nemen we Redis als voorbeeld om het schema te analyseren waarbij cache gedistribueerde locks worden geïmplementeerd.
Er zijn veel gerelateerde artikelen op het internet over het implementeren van gedistribueerde sloten gebaseerd op Redis, en de belangrijkste implementatiemethode is het gebruik van de Jedis.setNX-methode.
Er zijn ook verschillende problemen met bovenstaande implementatie:
1. Probleem met één punt.
2. Deze vergrendeling heeft geen vervaltijd; zodra de ontgrendelingsoperatie mislukt, zal het vergrendelingsrecord voortdurend in redis staan, en kunnen andere threads het slot niet langer verkrijgen.
3. Deze vergrendeling kan alleen niet-blokkerend zijn, en zal direct terugkeren, ongeacht succes of mislukking.
4. Dit slot is niet-re-terugtrekkend; nadat een thread het slot heeft verkregen, kan het het slot niet opnieuw krijgen voordat het wordt losgelaten, omdat de gebruikte sleutel al bestaat in redis. setNX-operaties kunnen niet langer worden uitgevoerd.
5. Deze lock is oneerlijk, alle wachtende threads starten setNX-operaties tegelijk, en gelukkige threads kunnen de lock krijgen.
Natuurlijk zijn er ook manieren om het op te lossen.
- Tegenwoordig ondersteunen reguliere cachingdiensten clusterimplementatie om single point-problemen op te lossen via clustering.
- Geen vervaltijd? De setExpire-methode van redis ondersteunt de inkomende vervaltijd, en de gegevens worden automatisch verwijderd nadat die tijd is bereikt.
- Niet-blokkerend? terwijl ze herhaaldelijk werden geëxecuteerd.
- Is het niet mogelijk om weer binnen te komen? Nadat een thread de lock heeft verkregen, sla je de huidige hostinformatie en threadinformatie op, en controleer je of je de eigenaar bent van de huidige lock voordat je deze de volgende keer verkrijgt.
- Oneerlijk? Zet alle wachtende threads in een wachtrij voordat een thread een lock krijgt, en verkrijg die lock vervolgens op een first-in, first-out basis.
Het synchronisatiebeleid van de redis-cluster kost tijd, en het is mogelijk dat thread A een lock krijgt na het succesvol instellen van NX, maar deze waarde is niet bijgewerkt naar de server waar thread B setNX uitvoert, wat gelijktijdigheidsproblemen veroorzaakt.
Salvatore Sanfilippo, de auteur van redis, stelde het Redlock-algoritme voor, dat gedistribueerd lock management (DLM) implementeert dat veiliger en betrouwbaarder is dan een enkele knoop.
Het Redlock-algoritme gaat ervan uit dat er N redis-knopen zijn die onafhankelijk van elkaar zijn, meestal ingesteld op N=5, en dat deze N knopen op verschillende machines draaien om fysieke onafhankelijkheid te behouden.
De stappen van het algoritme zijn als volgt:
1. De cliënt verkrijgt de huidige tijd in milliseconden. 2. De client probeert het slot van N knopen te verkrijgen (elke knoop krijgt het slot op dezelfde manier als het eerder genoemde cacheslot), en N knooppunten krijgen het slot met dezelfde sleutel en waarde. De client moet de interface-toegangstimeout instellen, en de interface-timeout moet veel korter zijn dan de locktimeout, bijvoorbeeld de automatisch loslatende lock is 10 seconden, daarna wordt de interface-timeout ingesteld op ongeveer 5-50ms. Dit stelt je in staat om zo snel mogelijk te time-outen bij het openen van een redis-node nadat deze uitvalt, en vermindert het normale gebruik van het slot. 3. De client berekent hoeveel tijd het kost om de lock te verkrijgen door de tijd die in stap 1 is verkregen af te trekken van de huidige tijd; alleen wanneer de client meer dan 3 nodes van de lock heeft verkregen, en de tijd om de lock te verkrijgen korter is dan de timeout-tijd van de lock, krijgt de client de gedistribueerde lock. 4. De tijd die de klant nodig heeft om het slot te verkrijgen is de ingestelde lock-timeouttijd minus de tijd die wordt besteed aan het verkrijgen van de lock, berekend in stap 3. 5. Als de client er niet in slaagt het slot te verkrijgen, verwijdert de client alle sloten achter elkaar. Met het Redlock-algoritme kan worden gegarandeerd dat de distributed lock-service nog steeds kan werken bij het ophangen van maximaal 2 nodes, wat de beschikbaarheid aanzienlijk verbetert ten opzichte van de vorige databaselock en cachelock.
Echter, een gedistribueerde expert schreef een artikel "How to do distributed locking" waarin hij de juistheid van Redlock in twijfel trok.
De expert gaf aan dat er twee aspecten zijn om rekening mee te houden bij het overwegen van distributed locks: prestaties en correctheid.
Als je een high-performance distributed lock gebruikt en de correctheid niet vereist is, dan is het gebruik van een cachelock voldoende.
Als je een zeer betrouwbare distributed lock gebruikt, moet je rekening houden met strikte betrouwbaarheidskwesties. Redlock daarentegen voldoet niet aan de juistheid. Waarom niet? Experts noemen verschillende aspecten.
Tegenwoordig gebruiken veel programmeertalen virtuele machines met GC-functies; in Full GC stopt het programma met het verwerken van GC, soms duurt Full GC lang, en zelfs het programma heeft een paar minuten vertraging; het artikel vermeldt het voorbeeld van HBase, HBase soms GC voor een paar minuten, waardoor de lease uitloopt. Bijvoorbeeld, in de onderstaande figuur krijgt client 1 een lock en staat op het punt een gedeelde resource te verwerken, en wanneer het op het punt staat een gedeelde resource te verwerken, vindt Full GC plaats totdat de lock verloopt. Op deze manier krijgt client 2 de lock opnieuw en begint te werken aan de gedeelde resource. Wanneer client 2 verwerkt, voltooit client 1 de volledige GC en begint met het verwerken van gedeelde resources, zodat beide clients gedeelde resources verwerken.
Experts gaven een oplossing, zoals te zien is in de onderstaande figuur, het lijkt MVCC, breng een token naar de lock, token is het concept van de versie, elke keer dat de operatievergrendeling is voltooid, wordt het token toegevoegd: 1, breng het token bij het verwerken van gedeelde resources, alleen de gespecificeerde versie van het token kan de gedeelde resource verwerken.
Daarna zei de expert ook dat het algoritme vertrouwt op lokale tijd, en dat Redis vertrouwt op de getTimeOfDay-methode om de tijd te krijgen in plaats van de monotone klok bij het verwerken van sleutelverval, wat ook leidt tot tijdsinnauwkeurigheden. Bijvoorbeeld, in een scenario hebben twee client 1 en client 2 5 redis-knooppunten (A, B, C, D en E).
1. Client 1 verkrijgt succesvol de vergrendeling van A, B en C, en krijgt de vergrendelingsnetwerktimeout van D en E. 2. De klok van knooppunt C is onnauwkeurig, wat de lock-timeout veroorzaakt. 3. client 2 verkrijgt succesvol het slot van C, D en E, en krijgt de timeout van het vergrendelingsnetwerk van A en B. 4. Op deze manier krijgen zowel client 1 als client 2 een lock. Om de twee punten die experts zeggen over de onbeschikbaarheid van Redlock samen te vatten:
1. GC en andere scenario's kunnen op elk moment optreden, waardoor de client een lock krijgt, en de verwerkingstimeout zorgt ervoor dat een andere client de lock verwint. Experts gaven ook een oplossing voor het gebruik van zelf-incrementerende tokens. 2. Het algoritme vertrouwt op lokale tijd, en de klok zal onnauwkeurig zijn, waardoor twee clients tegelijkertijd vergrendelingen krijgen. Daarom is de conclusie van experts dat Redlock alleen normaal kan werken binnen de begrensde netwerkvertraging, begrensde programmaonderbreking en begrensde klokfoutbereik, maar de grenzen van deze drie scenario's kunnen niet worden bevestigd, dus deskundigen raden Redlock niet aan. Voor scenario's met hoge correctheidseisen raden experts Zookeeper aan, dat later besproken zal worden met Zookeeper als een gedistribueerd slot.
Reactie van de auteur van Redis
De auteur van Redis reageerde door een blog te schrijven nadat hij het artikel van de expert had gezien. De auteur bedankte de expert beleefd en uitte vervolgens zijn onenigheid met het standpunt van de expert.
Ik vroeg om een analyse in de oorspronkelijke Redlock-specificatie hier:http://redis.io/topics/distlock.Dus bedankt, Martin. Toch ben ik het niet eens met de analyse.
De bespreking van de REDIS-auteur over het gebruik van tokens om het locktimeoutprobleem op te lossen, kan worden samengevat in de volgende vijf punten:
Punt 1, het gebruik van gedistribueerde sloten is meestal dat je geen andere manier hebt om gedeelde middelen te beheren, experts gebruiken tokens om de verwerking van gedeelde middelen te waarborgen, en dan is er geen behoefte aan gedistribueerde locks. Punt 2: Voor tokengeneratie heeft de service die tokens genereert nog steeds gedistribueerde sloten nodig om de betrouwbaarheid van tokens die door verschillende clients worden verkregen te waarborgen. Punt 3, voor de manier waarop experts zeggen dat zelf-incrementerende tokens, vindt de redis-auteur dat het volstrekt onnodig is, elke client kan een unieke uuid als token genereren en de gedeelde resource instellen op een toestand die alleen de client met de uuid kan verwerken, zodat andere clients de gedeelde resource niet kunnen verwerken totdat de client die het slot verkrijgt de lock loslaat. Zoals te zien is in de bovenstaande figuur, als de client van token 34 GC stuurt tijdens het schrijfproces en de lock laat verlopen, kan een andere client de lock van token 35 krijgen en opnieuw beginnen met schrijven, wat resulteert in een lock conflict. Daarom kan de volgorde van tokens niet worden gecombineerd met gedeelde middelen. Punt 5, de redis-auteur gelooft dat in de meeste scenario's gedistribueerde sloten worden gebruikt om updateproblemen in niet-transactionele situaties op te lossen. De auteur zou moeten zeggen dat er situaties zijn waarin het moeilijk is om tokens te combineren om gedeelde bronnen te verwerken, dus je moet vertrouwen op sloten om bronnen te vergrendelen en te verwerken. Een ander klokprobleem waar experts het over hebben, Redis auteurs geven ook een verklaring. Als de tijd die nodig is om het slot te verkrijgen te lang is en de standaard time-out tijd van de lock overschrijdt, kan de client de lock op dat moment niet verkrijgen en zullen er geen voorbeelden worden voorgesteld door experts.
Persoonlijke gevoelens
Het eerste probleem dat ik samenvat is dat nadat een client een gedistribueerde lock heeft verkregen, de lock kan worden vrijgegeven na een time-out tijdens de verwerking van de client. Eerder, bij het bespreken van de timeout die door de databasevergrendeling van 2 minuten wordt ingesteld, kan als een taak een orderlock langer dan 2 minuten in beslag neemt, het andere handelscentrum deze orderlock verkrijgen, zodat de twee handelscentra dezelfde order tegelijk kunnen verwerken. Normaal gesproken wordt de taak binnen enkele seconden verwerkt, maar soms is de timeout die wordt ingesteld door het joinen van een RPC-verzoek te lang en zijn er meerdere van zulke timeoutverzoeken in een taak, waardoor de automatische ontgrendelingstijd waarschijnlijk wordt overschreden. Als we in Java schrijven, kan er Full GC in het midden zijn, dus nadat het slot is ontgrendeld na de lock-timeout, kan de client het niet meer waarnemen, wat een heel ernstig probleem is. Ik denk niet dat dit een probleem is met het slot zelf, zolang elk hierboven genoemd gedistribueerd slot de kenmerken van timeout-release heeft, zal zo'n probleem optreden. Als je de lock timeout-functie gebruikt, moet de client de lock timeout instellen en dienovereenkomstig actie ondernemen, in plaats van de gedeelde resource verder te verwerken. Het algoritme van Redlock geeft de lock-tijd terug die de client kan bezetten nadat de client de lock heeft verkregen, en de client moet deze tijd verwerken om de taak daarna te stoppen.
Het tweede probleem is dat gedistribueerde experts Redlock niet begrijpen. Een belangrijk kenmerk van Redlock is dat de tijd om het slot te verkrijgen de totale tijd is dat het slot standaard time-out neemt minus de tijd die het kost om het slot te verkrijgen, zodat de tijd die de client nodig heeft om te verwerken relatief is, ongeacht de lokale tijd.
Vanuit dit perspectief kan de juistheid van Redlock goed worden gegarandeerd. Zorgvuldige analyse van Redlock, vergeleken met redis van een knooppunt, is de belangrijkste eigenschap van Redlock een hogere betrouwbaarheid, wat in sommige scenario's een belangrijke eigenschap is. Maar ik denk dat Redlock te veel geld heeft uitgegeven om betrouwbaarheid te bereiken.
- Ten eerste moeten er 5 nodes worden ingezet om Redlock betrouwbaarder te maken.
- Daarna moet je 5 nodes aanvragen om de lock te krijgen, en via de Future-methode kun je eerst gelijktijdig 5 nodes aanvragen en vervolgens het responsresultaat samenstellen, wat de responstijd kan verkorten, maar het kost nog steeds meer tijd dan een single-node redis lock.
- Omdat er dan meer dan 3 van de 5 knopen verkregen moeten worden, kan er een vergrendelingsconflict ontstaan, dat wil zeggen, iedereen heeft 1-2 vergrendelingen verkregen, en daardoor kan niemand het slot krijgen; dit probleem leent de auteur van Redis de essentie van het vlotalgoritme, door de botsing op willekeurig moment kan de conflicttijd sterk worden verkort, maar dit probleem kan niet goed worden vermeden, vooral wanneer het slot voor het eerst wordt verkregen, waardoor de tijdskosten voor het verkrijgen van het slot toenemen.
- Als 2 van de 5 nodes offline zijn, wordt de beschikbaarheid van de lock sterk verminderd; allereerst moet je wachten tot de resultaten van deze twee uitgeschakelde nodes uitlopen voordat je terugkeert, en er zijn maar 3 nodes, en de client moet de locks van alle 3 nodes verkrijgen om de lock te hebben, wat ook moeilijker is.
- Als er een netwerkpartitie is, kan het zijn dat de client de lock nooit kan verkrijgen.
Na het analyseren van zoveel redenen, denk ik dat het meest kritieke punt van Redlocks probleem is dat Redlock vereist dat clients de consistentie van schrijfacties waarborgen, en de 5 backend nodes volledig onafhankelijk zijn, en alle clients deze 5 nodes moeten bedienen. Als er een leider is tussen 5 knooppunten, kan de client de gegevens van de leider synchroniseren zolang de client de lock van de leider verkrijgt, zodat er geen problemen zijn zoals partitatie, time-outs en conflicten. Daarom denk ik dat het gebruik van een gedistribueerde coördinatiedienst met sterke consistentie het probleem beter kan oplossen om de correctheid van gedistribueerde locks te waarborgen.
De vraag rijst opnieuw: hoe lang moet ik de houdbaarheidstijd instellen? De invalidatietijd instellen is te kort, en de lock wordt automatisch vrijgegeven voordat de methode wordt uitgevoerd, waardoor er gelijktijdigheidsproblemen ontstaan. Als het te lang duurt, moeten andere threads die het slot krijgen misschien lang wachten.
Dit probleem bestaat ook bij het gebruik van databases om gedistribueerde sloten te implementeren.
De huidige gangbare aanpak voor dit probleem is om een korte timeout-tijd in te stellen voor elke verkregen lock, en een thread te starten om de locktime-out te verversen telkens wanneer de timeout bijna bereikt wordt. Beëindig deze thread tegelijk met het losmaken van het slot. Bijvoorbeeld, redisson, het officiële gedistribueerde slotonderdeel van redis, gebruikt deze oplossing.
Voordelen van het gebruik van caching om gedistribueerde locks te implementeren Goede prestatie.
Nadelen van het gebruik van caching om gedistribueerde sloten te implementeren De uitvoering is te verantwoordelijk, er zijn te veel factoren om rekening mee te houden.
Gedistribueerde sloten gebaseerd op Zookeeper-implementatie
Gedistribueerde sloten gebaseerd op tijdelijk georden knooppunten van Zookeeper.
Het algemene idee is dat wanneer elke client een methode vergrendelt, er een unieke instantane geordende node wordt gegenereerd in de map van de gespecificeerde node die overeenkomt met de methode op Zookeeper. De manier om te bepalen of je een lock moet krijgen is eenvoudig: je hoeft alleen degene met het kleinste serienummer in de geordte knoop te bepalen. Wanneer de vergrendeling wordt losgemaakt, verwijder je simpelweg de instantane node. Tegelijkertijd kan het het probleem van deadlocks door service-downtime die niet kunnen worden opgeheven, voorkomen.
Laten we kijken of Zookeeper de eerder genoemde problemen kan oplossen.
- Slot gaat niet los? Het gebruik van Zookeeper kan het probleem van het niet vrijgeven van sloten effectief oplossen, want bij het aanmaken van een slot maakt de client een tijdelijke node in ZK aan, en zodra de client het slot verkrijgt en plotseling hangt (de sessieverbinding is verbroken), wordt de tijdelijke node automatisch verwijderd. Andere klanten kunnen het slot weer krijgen.
- Niet-blokkerende sloten? Zodra de knoop verandert, zal Zookeeper de client informeren, en kan de client controleren of de knoop die hij heeft gemaakt het kleinste ordinaal getal van alle knopen is.
- Kunt u niet terugkeren? Wanneer de client een node aanmaakt, schrijft hij direct de hostinformatie en threadinformatie van de huidige client naar de node, en de volgende keer dat je de lock wilt krijgen, kun je deze vergelijken met de data in de huidige kleinste node. Als de informatie hetzelfde is als die van jou, kun je direct de lock verkrijgen, en als die anders is, een tijdelijke sequentiële node aanmaken om deel te nemen aan de wachtrij.
De vraag rijst opnieuw: we weten dat Zookeeper in clusters moet worden ingezet, zullen er problemen zijn met datasynchronisatie zoals bij Redis-clusters?
Zookeeper is een gedistribueerde component die zwakke consistentie garandeert, oftewel uiteindelijke consistentie.
Zookeeper gebruikt een data-synchronisatieprotocol genaamd Quorum Based Protocol. Als er N Zookeeper-servers in de Zookeeper-cluster zijn (N is meestal oneven, 3 kunnen aan databetrouwbaarheid voldoen en hebben hoge lees- en schrijfprestaties, en 5 hebben de beste balans tussen databetrouwbaarheid en lees- en schrijfprestaties), dan wordt een schrijfoperatie van de gebruiker eerst gesynchroniseerd met N/2 + 1 servers en vervolgens teruggegeven aan de gebruiker, waarbij de gebruiker wordt gevraagd succesvol te schrijven. Het data-synchronisatieprotocol gebaseerd op het Quorum Based Protocol bepaalt de consistentie van de sterkte die Zookeeper kan ondersteunen.
In een gedistribueerde omgeving is gegevensopslag die aan sterke consistentie voldoet vrijwel niet bestaand, en vereist dat alle knooppunten synchroon worden bijgewerkt wanneer de data van één knoop wordt bijgewerkt. Deze synchronisatiestrategie verschijnt in de master-slave synchrone replicatiedatabase. Deze synchronisatiestrategie heeft echter te veel invloed op de schrijfprestaties en komt zelden voor in de praktijk. Omdat Zookeeper N/2+1 knooppunten synchroon schrijft en N/2 knooppunten niet synchroon worden bijgewerkt, is Zookeeper niet sterk consistent.
De gegevensupdate-operatie van de gebruiker garandeert niet dat volgende reads de bijgewerkte waarde zullen lezen, maar zal uiteindelijk consistentie tonen. Het opofferen van consistentie betekent niet dat je de consistentie van de data volledig negeert, anders is de data chaotisch, dus hoe hoog de systeembeschikbaarheid ook is, hoe goed de distributie ook is, het is waardeloos. Het opofferen van consistentie is gewoon dat sterke consistentie in relationele databases niet langer nodig is, maar zolang het systeem uiteindelijke consistentie kan bereiken.
Een vraag met één punt? Het gebruik van Zookeeper kan effectief een probleem van één punt oplossen; ZK wordt in clusters ingezet; zolang meer dan de helft van de machines in de cluster overleeft, kan de dienst aan de buitenwereld worden geleverd.
Eerlijkheidsproblemen? Het gebruik van Zookeeper kan het probleem van eerlijke locks oplossen, de tijdelijke nodes die door de client in ZK zijn aangemaakt zijn ordelijk, en elke keer dat de lock wordt losgelaten, kan ZK de kleinste node waarschuwen om de lock te verkrijgen, wat eerlijkheid waarborgt.
De vraag rijst opnieuw: we weten dat Zookeeper in clusters moet worden ingezet, zullen er problemen zijn met datasynchronisatie zoals bij Redis-clusters?
Zookeeper is een gedistribueerde component die zwakke consistentie garandeert, oftewel uiteindelijke consistentie.
Zookeeper gebruikt een data-synchronisatieprotocol genaamd Quorum Based Protocol. Als er N Zookeeper-servers in de Zookeeper-cluster zijn (N is meestal oneven, 3 kunnen aan databetrouwbaarheid voldoen en hebben hoge lees- en schrijfprestaties, en 5 hebben de beste balans tussen databetrouwbaarheid en lees- en schrijfprestaties), dan wordt een schrijfoperatie van de gebruiker eerst gesynchroniseerd met N/2 + 1 servers en vervolgens teruggegeven aan de gebruiker, waarbij de gebruiker wordt gevraagd succesvol te schrijven. Het data-synchronisatieprotocol gebaseerd op het Quorum Based Protocol bepaalt de consistentie van de sterkte die Zookeeper kan ondersteunen.
In een gedistribueerde omgeving is gegevensopslag die aan sterke consistentie voldoet vrijwel niet bestaand, en vereist dat alle knooppunten synchroon worden bijgewerkt wanneer de data van één knoop wordt bijgewerkt. Deze synchronisatiestrategie verschijnt in de master-slave synchrone replicatiedatabase. Deze synchronisatiestrategie heeft echter te veel invloed op de schrijfprestaties en komt zelden voor in de praktijk. Omdat Zookeeper N/2+1 knooppunten synchroon schrijft en N/2 knooppunten niet synchroon worden bijgewerkt, is Zookeeper niet sterk consistent.
De gegevensupdate-operatie van de gebruiker garandeert niet dat volgende reads de bijgewerkte waarde zullen lezen, maar zal uiteindelijk consistentie tonen. Het opofferen van consistentie betekent niet dat je de consistentie van de data volledig negeert, anders is de data chaotisch, dus hoe hoog de systeembeschikbaarheid ook is, hoe goed de distributie ook is, het is waardeloos. Het opofferen van consistentie is gewoon dat sterke consistentie in relationele databases niet langer nodig is, maar zolang het systeem uiteindelijke consistentie kan bereiken.
Of Zookeeper causale consistentie bereikt, hangt af van hoe de client is geprogrammeerd.
Praktijken die niet voldoen aan causale consistentie
- Proces A schrijft een stuk data naar Zookeeper's /z en keert succesvol terug
- Proces A informeert proces B dat A de gegevens van /z heeft aangepast
- B leest de gegevens van Zookeeper's /z
- Aangezien de server van de Zookeeper die met B is verbonden mogelijk niet is bijgewerkt met de geschreven data van A, kan B de geschreven data van A niet lezen
Praktijken die causale consistentie volgen
- Proces B luistert naar datawijzigingen in /z op Zookeeper
- Proces A schrijft een stuk data naar Zookeeper's /z, en voordat het succesvol terugkeert, moet Zookeeper de luisteraar oproepen die op /z is geregistreerd, en de leider zal B op de hoogte brengen van de wijziging in de data
- Nadat de event response-methode van proces B is beantwoord, neemt het de gewijzigde data, dus B zal zeker de gewijzigde waarde kunnen krijgen
- Causale consistentie verwijst hier naar de causale consistentie tussen Leider en B, dat wil zeggen, de leider meldt de gegevens van een verandering
Het tweede event listening-mechanisme is ook de methode die gebruikt moet worden om Zookeeper correct te programmeren, dus Zookeeper moet voldoen aan de causale consistentie
Daarom moeten we, wanneer we gedistribueerde locks implementeren op basis van Zookeeper, de praktijk van causale consistentie gebruiken, dat wil zeggen dat de threads die op de lock wachten luisteren naar de wijzigingen in de lock van Zookeeper, en wanneer de lock wordt vrijgegeven, zal Zookeeper de wachtende thread die aan de fair lock-voorwaarden voldoet, informeren.
Je kunt direct de Zookeeper derdepartij bibliotheekclient gebruiken, die een reentrant lock-service encapsuleert.
Gedistribueerde locks geïmplementeerd met ZK lijken precies te passen bij wat we aan het begin van dit artikel van een gedistribueerde lock verwachtten. Dat is echter niet zo, en de gedistribueerde vergrendeling die door Zookeeper is geïmplementeerd heeft eigenlijk een nadeel, namelijk dat de prestaties mogelijk niet zo hoog zijn als die van de cachingservice. Omdat elke keer tijdens het creëren en openen van een lock, instantane nodes dynamisch moeten worden aangemaakt en vernietigd om de lock-functie te realiseren. Het aanmaken en verwijderen van knooppunten in ZK kan alleen via de leader-server worden uitgevoerd, waarna de data wordt gedeeld met alle volgersmachines.
Voordelen van het gebruik van Zookeeper om gedistribueerde sloten te implementeren Los effectief problemen met één punt, niet-terugkeer, niet-blokkerende problemen en het ontgrendelen van het slot op. Het is relatief eenvoudig te implementeren.
Nadelen van het gebruik van Zookeeper om gedistribueerde sloten te implementeren De prestaties zijn niet zo goed als het gebruik van cache om gedistribueerde locks te implementeren. Inzicht in de principes van ZK is vereist.
Vergelijking van de drie opties
Vanuit het perspectief van begrijpelijkheid (van laag naar hoog) Database > Cache > Zookeeper
Vanuit het perspectief van implementatiecomplexiteit (van laag naar hoog) Zookeeper > cache > databases
Vanuit een prestatieperspectief (van hoog naar laag) Caching > Zookeeper >= database
Vanuit betrouwbaarheidsperspectief (van hoog naar laag) Zookeeper > cache > databases
|