Ferspraat slot mei help fan Redis

Hoi Habr!

Hjoed bringe wy jo oandacht in oersetting fan in kompleks artikel oer de ymplemintaasje fan ferdielde beskoatteljen mei Redis en noegje jo út om te praten oer de perspektiven fan Redis as ûnderwerp. Analyse fan it Redlock-algoritme yn kwestje fan Martin Kleppmann, skriuwer fan it boek "Applikaasjes mei hege lading", jûn hjir.

Ferspraat beskoatteljen is in heul nuttich primitive dat wurdt brûkt yn in protte omjouwings wêr't ferskate prosessen moatte wurkje oan dielde boarnen op in ûnderling eksklusyf manier.

D'r binne in oantal biblioteken en berjochten dy't beskriuwe hoe't jo DLM (Distributed Lock Manager) kinne ymplementearje mei Redis, mar elke bibleteek nimt in oare oanpak en de garânsjes dy't se leverje binne frij swak yn ferliking mei wat te berikken is mei wat mear ferfine ûntwerp.

Yn dit artikel sille wy besykje in betingst kanonike algoritme te beskriuwen dy't toant hoe't jo ferdielde beskoatteljen kinne ymplementearje mei Redis. Wy sille prate oer in algoritme neamd Redlock, it ymplementearret in ferdield slot manager en, yn ús miening, dit algoritme is feiliger as de gewoane single-ynstân oanpak. Wy hoopje dat de mienskip it sil analysearje, feedback jaan en it as útgongspunt brûke foar mear komplekse as alternative projekten.

Ymplemintaasje

Foardat jo trochgean nei de beskriuwing fan it algoritme, jouwe wy ferskate keppelings nei klearmakke ymplemintaasjes. Se kinne brûkt wurde foar referinsje.

Feiligens en beskikberens garânsjes

Wy sille ús ûntwerp modellearje mei mar trije eigenskippen dy't neffens ús de minimale garânsjes leverje dy't nedich binne om effektyf ferdield beskoatteljen te brûken.

  1. Feiligens eigendom: Wjersidige útsluting. Op elts momint kin mar ien klant it slot hâlde.
  2. Beskikberens Eigenskip A: Gjin deadlocks. It is altyd mooglik om úteinlik in slot te krijen, sels as de kliïnt dy't de boarne beskoattele mislearret of op in oar skiifsegment komt.
  3. Beskikberens Eigenskip B: Fault Tolerance. Salang't de mearderheid fan Redis-knooppunten rint, kinne kliïnten slûzen krije en loslitte.

Wêrom ymplemintaasje basearre op mislearring herstel is net genôch yn dit gefal
Om te begripen wat wy sille ferbetterje, litte wy de hjoeddeistige stân fan saken analysearje mei de meast ferspraat slûsbibleteken basearre op Redis.

De ienfâldichste manier om in boarne te beskoatteljen mei Redis is it meitsjen fan in kaai yn it eksimplaar. Typysk wurdt in kaai makke mei in beheind libben, dit wurdt berikt mei de ferrinfunksje dy't yn Redis oanbean wurdt, dus ier of letter wurdt dizze kaai frijjûn (eigendom 2 yn ús list). As de kliïnt de boarne moat loslitte, wisket it de kaai.

Op it earste each wurket dizze oplossing frij goed, mar d'r is in probleem: ús arsjitektuer soarget foar ien punt fan mislearring. Wat bart der as de host Redis-eksimplaar mislearret? Lit ús dan in slaaf tafoegje! En wy sille it brûke as de presintator net beskikber is. Spitigernôch is dizze opsje net libbensfetber. Troch dit te dwaan, sille wy it eigendom fan ûnderlinge útsluting net goed kinne ymplementearje dat wy nedich binne om feiligens te garandearjen, om't replikaasje yn Redis asynchronous is.

Fansels komt yn sa'n model in racebetingst foar:

  1. Opdrachtgever A krijt in slot op 'e master.
  2. De master mislearret foardat de kaai yngong wurdt oerdroegen oan de slaaf.
  3. De folger wurdt promovearre ta lieder.
  4. Kliïnt B krijt in slot op deselde boarne dy't A al hat beskoattele. FEILIGHEIDSSKINNING!

Soms is it folslein normaal dat yn bysûndere omstannichheden, lykas in mislearring, in protte kliïnten it slot tagelyk hâlde kinne. Yn sokke gefallen kin in replikaasje-basearre oplossing tapast wurde. Oars, riede wy de oplossing beskreaun yn dit artikel.

Korrekte ymplemintaasje mei ien eksimplaar

Foardat wy besykje de tekoarten fan 'e hjirboppe beskreaune konfiguraasje mei ien eksimplaar te oerwinnen, litte wy begripe hoe't jo dit ienfâldige gefal goed kinne behannelje, om't dizze oplossing eins jildich is yn applikaasjes wêr't in racebetingst fan tiid ta tiid akseptabel is, en ek om't blokkearjen op in ien eksimplaar tsjinnet as de basis dy't wurdt brûkt yn it ferspraat algoritme beskreaun hjir.

Om in slot te krijen, dwaan dit:

SET resource_name my_random_value NX PX 30000

Dit kommando ynstallearret in kaai allinich as it net al bestiet (NX-opsje), mei in jildigensperioade fan 30000 millisekonden (PX-opsje). De kaai is ynsteld op "myrandomvalue" Dizze wearde moat unyk wêze foar alle kliïnten en alle slotfersiken.
Yn prinsipe wurdt in willekeurige wearde brûkt om it slot feilich los te litten, mei in skript dat Redis fertelt: ferwiderje de kaai allinich as it bestiet en de wearde dy't dêryn opslein is, is krekt wat ferwachte waard. Dit wurdt berikt mei it folgjende Lua-skript:

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

Dit is wichtich om foar te kommen dat in slot holden troch in oare klant wurdt fuortsmiten. Bygelyks, in klant kin in slot krije, dan opsletten wurde yn in operaasje dy't langer duorret dan it earste slot (sadat de kaai tiid hat om te ferrinnen), en letter it slot fuortsmite dat in oare klant hie pleatst.
It brûken fan in ienfâldige DEL is ûnfeilich omdat in klant kin fuortsmite in slot holden troch in oare klant. Yn tsjinstelling, by it brûken fan it boppesteande skript, wurdt elk slot "ûndertekene" mei in willekeurige tekenrige, sadat allinich de kliïnt dy't it earder pleatst it kin fuortsmite.

Wat moat dizze willekeurige tekenrige wêze? Ik tink dat it 20 bytes wêze moat fan /dev/urandom, mar jo kinne minder djoere manieren fine om de tekenrige unyk genôch te meitsjen foar jo doelen. It soe bygelyks goed wêze om RC4 te sieden mei /dev/urandom en dan in pseudo-willekeurige stream derút te generearjen. In ienfâldiger oplossing omfettet in kombinaasje fan unix tiid yn mikrosekonde resolúsje plus de client ID; it is net sa feilich, mar it is nei alle gedachten oan 'e taak yn de measte konteksten.

De tiid dy't wy brûke as mjitte fan 'e libbenslange fan' e kaai wurdt de "slûslibben" neamd. Dizze wearde is sawol de hoemannichte tiid foardat it slot automatysk wurdt loslitten as de tiid dy't in klant hat om in operaasje te foltôgjen foardat in oare klant dizze boarne op syn beurt kin beskoattelje, sûnder feitlik ynbreuk op garânsjes foar ûnderlinge útsluting. Dizze garânsje is beheind allinnich ta in bepaald finster fan tiid, dat begjint út it momint dat it slot wurdt kocht.

Dat wy hawwe in goede manier besprutsen om in slot te krijen en frij te litten. It systeem (as wy it hawwe oer in net-ferspraat systeem besteande út ien en altyd beskikber eksimplaar) is feilich. Litte wy dit konsept útwreidzje nei in ferspraat systeem, wêr't wy gjin sokke garânsjes hawwe.

Redlock algoritme

De ferdielde ferzje fan it algoritme giet derfan út dat wy N Redis-masters hawwe. Dizze knopen binne folslein ûnôfhinklik fan elkoar, dus wy brûke gjin replikaasje of in oar ymplisyt koördinaasjesysteem. Wy hawwe al behannele hoe't jo in slûs feilich kinne krije en loslitte op ien eksimplaar. Wy nimme it foar fanselssprekkend dat it algoritme, by it wurkjen mei ien eksimplaar, krekt dizze metoade sil brûke. Yn ús foarbylden sette wy N op 5, wat in ridlike wearde is. Sa sille wy 5 Redis-masters moatte brûke op ferskate kompjûters as firtuele masines om te soargjen dat se foar in grut part ûnôfhinklik fan elkoar hannelje.

Om in slot te krijen, fiert de klant de folgjende operaasjes út:

  1. Kriget de aktuele tiid yn millisekonden.
  2. Sequentially besiket te krijen in slot op alle N eksimplaren, mei help fan deselde kaai namme en willekeurige wearden yn alle gefallen. Yn Stage 2, as de kliïnt in slûs op in per-eksimplaar basis ynstelt, brûkt de kliïnt in fertraging om it te krijen dy't koart genôch is yn ferliking mei de tiid wêrnei't it slot automatysk wurdt loslitten. Bygelyks, as de blokkearjende doer 10 sekonden is, dan kin de fertraging yn it berik wêze fan ~5-50 millisekonden. Dit elimineert de situaasje wêryn de klant koe bliuwe blokkearre foar in lange tiid besykje te berikken in mislearre Redis knooppunt: as de eksimplaar is net beskikber, dan wy besykje te ferbinen mei in oare eksimplaar sa gau mooglik.
  3. Om in slot te nimmen, berekkent de kliïnt hoefolle tiid ferrûn is; Om dit te dwaan, lûkt it fan 'e eigentlike tiidwearde it tiidstempel dat waard krigen yn stap 1. As en allinich as de kliïnt it slot koe krije op 'e mearderheid fan eksimplaren (op syn minst 3), en de totale tiid dy't it naam om krije it slot, minder as it slot doer, it slot wurdt beskôge te hawwen krigen.
  4. As in slûs is oankocht, wurdt de slûsdoer beskôge as de orizjinele slûsdoer minus de ferrûne tiid berekkene yn stap 3.
  5. As de klant om ien of oare reden net slagget om it slot te krijen (of it koe N/2+1-eksimplaren net beskoattelje, of de slûsdoer wie negatyf), dan sil it besykje alle gefallen te ûntsluten (sels dejingen dy't hy tocht dat it net koe blokkearje ).

Is it algoritme asyngroan?

Dit algoritme is basearre op de oanname dat, hoewol d'r gjin syngronisearre klok is wêrop alle prosessen wurkje, lokale tiid yn elk proses noch yn likernôch itselde tempo streamt, en de flater is lyts yn ferliking mei de totale tiid wêrnei't it slot is automatysk útbrocht. Dizze oanname is tige ferlykber mei de situaasje dy't typysk is foar gewoane kompjûters: elke kompjûter hat in lokale klok, en wy kinne meastentiids rekkenje op it feit dat it tiidferskil tusken ferskate kompjûters lyts is.

Op dit punt moatte wy ús regel foar ûnderlinge útsluting foarsichtiger formulearje: ûnderlinge útsluting is allinich garandearre as de klant dy't it slot hâldt útgiet yn 'e tiid dat it slot jildich is (dizze wearde krigen yn stap 3), minus wat mear tiid (totaal in pear millisekonden om it tiidferskil tusken prosessen te kompensearjen).

It folgjende nijsgjirrige artikel fertelt mear oer sokke systemen dy't koördinaasje fan tiidintervallen fereaskje: Leases: in effisjint fouttolerant meganisme foar konsistinsje fan ferdielde triemcache.

Opnij besykje op mislearring

As in kliïnt net slagget om in slot te krijen, moat it nei in willekeurige fertraging nochris besykje; dit wurdt dien om de-syngronisaasje meardere kliïnten besykje te krijen in slot op deselde boarne tagelyk (wat kin liede ta in "split-brain" situaasje dêr't der binne gjin winners). Derneist, hoe flugger in kliïnt besiket in slot te krijen op in mearderheid fan Redis-eksimplaren, hoe smeller it finster wêryn in split-brain-situaasje kin foarkomme (en hoe minder de needsaak foar opnij besykjen). Dêrom, by útstek, de kliïnt moat besykje te stjoeren SET kommando's nei N eksimplaren tagelyk mei help fan multiplexing.

It is de muoite wurdich om hjir te ûnderstreekjen hoe wichtich it is foar kliïnten dy't de mearderheid fan de slûzen net krije om de (foar in part) oankochte slûzen los te meitsjen, sadat se net hoege te wachtsjen oant de kaai ferrint foardat de slûs op de boarne wer oankocht wurde kin. (hoewol as netwurkfragmentaasje optreedt, en de klant ferliest kontakt mei de Redis-eksimplaren, dan moatte jo in beskikberensboete betelje wylst jo wachtsje op de kaai om te ferrinnen).

Los it slot los

It loslitten fan in slûs is in ienfâldige operaasje dy't gewoan it ûntskoatteljen fan alle gefallen fereasket, nettsjinsteande oft de kliïnt in bepaalde eksimplaar mei súkses liket te hawwen beskoattele.

Feiligens oerwagings

Is it algoritme feilich? Litte wy besykje foar te stellen wat der bart yn ferskate senario's.

Om te begjinnen mei, lit ús oannimme dat de klant koe krije in slot op de mearderheid fan gefallen. Eltse eksimplaar sil befetsje in kaai mei deselde libbensdoer foar alle. Elk fan dizze toetsen waard lykwols op in oare tiid ynstalleare, sadat se op ferskate tiden ferrinne. Mar, as de earste kaai waard ynstallearre op in tiid net slimmer dan T1 (de tiid dy't wy kieze foardat kontakt op mei de earste tsjinner), en de lêste kaai waard ynstallearre op in tiid net slimmer dan T2 (de tiid dat it antwurd waard ûntfongen fan 'e lêste tsjinner), dan binne wy ​​der wis fan dat de earste kaai yn' e set dy't ferrint op syn minst sil oerlibje MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT. Alle oare kaaien sille letter ferrinne, dus wy kinne der wis fan wêze dat alle kaaien tagelyk jildich binne foar op syn minst dizze tiid.

Yn 'e tiid dat de measte kaaien jildich bliuwe, sil in oare kliïnt it slot net kinne krije, om't N/2+1 SET NX-operaasjes net slagje kinne as N/2+1-kaaien al besteane. Dêrom, as in slot is oanskaft, is it ûnmooglik om it op itselde momint wer te krijen (dit soe yn striid wêze mei it eigendom fan ûnderlinge útsluting).
Wy wolle lykwols derfoar soargje dat meardere kliïnten dy't besykje in slot tagelyk te krijen, net tagelyk slagje.

As de kliïnt de mearderheid fan gefallen foar sawat of mear as de maksimale slûsdoer hat beskoattele, sil it de slûs ûnjildich beskôgje en de eksimplaren ûntsluten. Dêrom hoege wy allinich rekken te hâlden mei it gefal wêryn de kliïnt it slagge om de mearderheid fan gefallen te blokkearjen yn in tiid minder dan de ferfaldatum. Yn dit gefal, oangeande it boppesteande argumint, yn 'e tiid MIN_VALIDITY gjin klant moat wêze kinne reacquire it slot. Dêrom sille in protte kliïnten N / 2 + 1-eksimplaren tagelyk kinne beskoattelje (dy't einiget oan 'e ein fan poadium 2) allinich as de tiid om de mearderheid te sluten grutter wie as de TTL-tiid, wat it slot ûnjildich makket.

Kinne jo in formeel bewiis fan feiligens leverje, besteande ferlykbere algoritmen oanjaan, of in brek fine yn it boppesteande?

Tagonklikheid oerwagings

Systeembeskikberens hinget ôf fan trije wichtichste skaaimerken:

  1. Automatysk slûzen loslitte (as kaaien ferrinne): Kaaien sille úteinlik wer beskikber wêze om te brûken foar slûzen.
  2. It feit dat kliïnten meastentiids helpe inoar troch it fuortheljen fan slûzen doe't de winske slot is net oanskaft, of is oankocht en de baan is foltôge; dus it is wierskynlik dat wy net hoege te wachtsjen oant de kaaien ferrinne om it slot wer te krijen.
  3. It feit dat as in klant opnij moat besykje in slot te krijen, it wachtet op in fergelykber langere tiid dan de perioade dy't nedich is om de measte slûzen te krijen. Dit ferleget de kâns op in split-brain-situaasje dy't ûntstiet by konkurrearjen om boarnen.

D'r is lykwols in beskikberensstraf gelyk oan de TTL fan 'e netwurksegminten, dus as d'r oanienlizzende segminten binne, kin de straf ûnbepaald wêze. Dit bart as in klant in slot krijt en dan nei in oar segmint ript foardat it it kin loslitte.

Yn prinsipe, jûn ûneinige oanswettende netwurk segminten, in systeem kin bliuwe ûnbeskikber foar in ûneinige perioade fan tiid.

Prestaasje, failover en fsync

In protte minsken brûke Redis om't se hege prestaasjes fan slotserver nedich binne yn termen fan 'e latency dy't nedich is om slûzen te krijen en los te meitsjen, en it oantal oanwinsten / releases dat per sekonde kin wurde foltôge. Om oan dizze eask te foldwaan, is d'r in strategy om te kommunisearjen mei N Redis-tsjinners om latency te ferminderjen. Dit is in multiplexingstrategy (as "multiplexing fan earme man", wêrby't de socket yn net-blokkearjende modus set wurdt, alle kommando's ferstjoert en de kommando's letter lêst, oannommen dat de rûnreistiid tusken de kliïnt en elke eksimplaar ferlykber is) .

Wy moatte lykwols ek rekken hâlde mei de konsideraasje dy't ferbûn is mei gegevens opslach op lange termyn as wy stribje om in model te meitsjen mei betrouber herstel fan mislearrings.

Yn prinsipe, om it probleem te ferdúdlikjen, litte wy oannimme dat wy Redis konfigurearje mei hielendal gjin gegevensopslach op lange termyn. De kliïnt slagget 3 fan 5 eksimplaren te blokkearjen. Ien fan 'e eksimplaren dy't de kliïnt slagge om te blokkearjen wurdt opnij starte, en op dit stuit binne d'r 3 eksimplaren wer foar deselde boarne, dy't wy kinne blokkearje, en in oare kliïnt kin op syn beurt it opnij starte eksimplaar blokkearje, en it befeiligingseigenskip skeine dat giet út fan eksklusiviteit fan slûzen.

As jo ​​gegevens foarút (AOF) ynskeakelje, sil de situaasje in bytsje ferbetterje. Jo kinne bygelyks in tsjinner befoarderje troch it SHUTDOWN-kommando te stjoeren en it opnij te begjinnen. Sûnt ferfaloperaasjes yn Redis semantysk wurde ymplementearre op sa'n manier dat de tiid trochgiet, sels as de tsjinner is útskeakele, binne al ús easken goed. Dit is normaal sa lang as in normale shutdown wurdt garandearre. Wat te dwaan yn gefal fan stroomûnderbrekking? As Redis standert is ynsteld, mei fsync syngronisearje op skiif elke sekonde, dan is it mooglik dat wy nei in trochstart ús kaai net hawwe. Teoretysk, as wy slûsfeiligens wolle garandearje by elke werstart, moatte wy ynskeakelje fsync=always yn de ynstellings foar lange-termyn gegevens opslach. Dit sil de prestaasjes folslein deadzje, oant it nivo fan CP-systemen dy't tradisjoneel wurde brûkt om ferspraat slûzen feilich út te fieren.

Mar de situaasje is better as it liket op it earste each. Yn prinsipe wurdt de feiligens fan it algoritme bewarre bleaun, om't as de eksimplaar nei in mislearring opnij starte, it net mear dielnimme oan in slot dat op it stuit aktyf is.

Om dit te garandearjen, hoege wy gewoan te soargjen dat de eksimplaar nei in mislearring net beskikber bliuwt foar in perioade fan tiid dy't de maksimale TTL dy't wy brûke in bytsje grutter is. Op dizze manier sille wy wachtsje oant de ferfaldatum en automatyske frijlitting fan alle kaaien dy't aktyf wiene op it momint fan mislearring.

Mei help fan fertrage opstarten is it yn prinsipe mooglik om feiligens te berikken, sels by it ûntbrekken fan langduorjende persistinsje yn Redis. Tink derom dat dit kin resultearje yn in boete foar ynbreuk op tagonklikens. Bygelyks, as de mearderheid fan eksimplaren mislearret, sil it systeem wrâldwiid net beskikber wurde foar de TTL (en gjin boarne kin yn dizze tiid blokkearre wurde).

Wy fergrutsje de beskikberens fan it algoritme: wy ferlingje it blokkearjen

As it wurk útfierd troch kliïnten bestiet út lytse stappen, is it mooglik om te ferminderjen de standert slot doer en útfiere in meganisme foar it útwreidzjen fan slûzen. Yn prinsipe, as de kliïnt dwaande is mei komputearjen en de wearde fan it slotferfal is gefaarlik leech, kinne jo in Lua-skript nei alle gefallen stjoere dy't de TTL fan 'e kaai útwreidet as de kaai noch bestiet en syn wearde noch in willekeurige wearde is dy't wurdt krigen as de slot waard oankocht.

In klant moat allinich beskôgje dat in slûs opnij kocht wurdt as it slagge is om de mearderheid fan gefallen binnen de jildigensperioade te sluten.

Wier, technysk feroaret it algoritme net, dus it maksimum oantal werhelle besykjen om slûzen te krijen moat beheind wurde, oars sille de tagonklikenseigenskippen wurde skeind.

Boarne: www.habr.com

Add a comment