Bloccu distribuitu cù Redis

Ehi Habr!

Oghje portamu à a vostra attenzione una traduzzione di un articulu cumplessu nantu à l'implementazione di chjusi distribuiti cù Redis è vi invitamu à parlà di e prospettive di Redis cum'è tema. Analisi di l'algoritmu Redlock in quistione da Martin Kleppmann, autore di u libru "Applicazioni High Load", datu ccà.

U chjusu distribuitu hè un primitivu assai utile utilizatu in parechji ambienti induve e diverse prucessi devenu travaglià nantu à e risorse spartute in una manera mutuamente esclusiva.

Ci hè una quantità di biblioteche è posti chì descrizanu cumu implementà DLM (Distributed Lock Manager) cù Redis, ma ogni biblioteca piglia un approcciu diversu è e garanzii chì furnisce sò abbastanza debuli cumparatu cù ciò chì hè ottenibile cun un disignu un pocu più sofisticatu.

In questu articulu, pruvaremu à discrive un algoritmu canonicu cundizionale chì mostra cumu implementà u bloccu distribuitu cù Redis. Avemu da parlà di un algoritmu chjamatu Redlock, implementa un gestore di serratura distribuitu è, in u nostru parè, questu algoritmu hè più sicuru ch'è l'approcciu di solitu unicu. Speremu chì a cumunità l'analizà, furnisce feedback, è l'utilizarà cum'è puntu di partenza per prughjetti più cumplessi o alternativi.

Implementazione

Prima di passà à a descrizzione di l'algoritmu, furnimu parechji ligami per implementazioni pronti. Puderanu esse usatu per riferimentu.

Garanzia di Seguretat è Disponibilità

Andemu à modellà u nostru disignu cù solu trè proprietà chì pensemu chì furnisce i garanti minimi necessarii per aduprà in modu efficace u chjusu distribuitu.

  1. Pruprietà di sicurità: Esclusione mutuale. In ogni mumentu, solu un cliente pò mantene a serratura.
  2. Disponibilità Proprietà A: Nisun bloccu. Hè sempre pussibule acquistà eventualmente una serratura, ancu s'ellu u cliente chì hà chjusu a risorsa falla o sbarca in un segmentu di discu differenti.
  3. Pruprietà di Disponibilità B: Tolleranza di Fault. Sempre chì a maiò parte di i nodi Redis sò in esecuzione, i clienti sò capaci di acquistà è liberate serrature.

Perchè l'implementazione basata nantu à a ricuperazione di fallimentu ùn hè micca abbastanza in questu casu
Per capisce ciò chì andemu à migliurà, analizemu u statu attuale di l'affari cù a maiò parte di e librerie di chjusi distribuite basate in Redis.

A manera più simplice di chjude una risorsa cù Redis hè di creà una chjave in l'istanza. Di genere, una chjave hè creata cù una vita limitata, questu hè ottenutu utilizendu a funzione di scadenza furnita in Redis, cusì prima o dopu sta chjave hè liberata (proprietà 2 in a nostra lista). Quandu u cliente hà bisognu di liberà a risorsa, sguassate a chjave.

À u primu sguardu, sta suluzione funziona abbastanza bè, ma ci hè un prublema: a nostra architettura crea un unicu puntu di fallimentu. Chì succede se l'istanza Redis di l'ospite falla? Allora aghjunghjemu un schiavu ! È l'avemu aduprà se u presentatore ùn hè micca dispunibule. Sfortunatamente, sta opzione ùn hè micca viable. Fendu questu, ùn pudemu micca implementà bè a pruprietà di l'exclusione mutuale chì avemu bisognu di assicurà a sicurità, perchè a replicazione in Redis hè asincrona.

Ovviamente, in un tali mudellu una cundizione di razza si trova:

  1. U Cliente A acquista una serratura nantu à u maestru.
  2. U maestru falla prima chì l'entrata chjave hè trasferita à u slave.
  3. U seguitore hè prumuvutu à u capu.
  4. U Cliente B acquista una serratura nantu à a stessa risorsa chì A hà digià chjusu. VIOLAZIONE DI SICUREZZA!

Calchì volta hè cumplettamente normale chì in circustanze spiciali, cum'è un fallimentu, parechji clienti ponu tene u serratura à u stessu tempu. In tali casi, una soluzione basata in replicazione pò esse applicata. Altrimenti, ricumandemu a suluzione descritta in questu articulu.

Implementazione curretta cù una sola istanza

Prima di pruvà à superà i difetti di a cunfigurazione di un'istanza unica descritta sopra, capiscemu cumu trattà bè stu casu simplice, postu chì sta suluzione hè veramente valida in l'applicazioni induve una cundizione di razza hè accettabile da u tempu à u tempu, è ancu perchè u bloccu nantu à un una sola istanza serve cum'è a basa chì hè utilizata in l'algoritmu distribuitu descrittu quì.

Per acquistà una serratura, fate questu:

SET resource_name my_random_value NX PX 30000

Stu cumanda stalla una chjave solu s'ellu ùn esiste micca (opzione NX), cù un periodu di validità di 30000 millisecondi (opzione PX). A chjave hè impostata à "myrandomvalue" Stu valore deve esse unicu in tutti i clienti è tutte e richieste di serratura.
In fondu, un valore aleatoriu hè utilizatu per liberà a serratura in modu sicuru, cù un script chì dice à Redis: sguassate solu a chjave s'ellu esiste è u valore guardatu in questu hè esattamente ciò chì era previstu. Questu hè ottenutu usendu u seguente script Lua:

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

Questu hè impurtante per impedisce chì una serratura tenuta da un altru cliente sia eliminata. Per esempiu, un cliente puderia acquistà un chjusu, dopu esse chjusu in una certa operazione chì dura più di u primu chjusu (perchè a chjave hà u tempu di scadenza), è più tardi sguassate u chjusu chì un altru cliente hà postu.
Utilizà un DEL simplice ùn hè micca sicuru perchè un cliente pò sguassà una serratura tenuta da un altru cliente. In cuntrastu, quandu si usa l'script di sopra, ogni serratura hè "firmata" cù una stringa aleatoria, cusì solu u cliente chì hà postu prima pò sguassà.

Chì duverebbe esse sta stringa casuale ? Pensu chì deve esse 20 bytes da /dev/urandom, ma pudete truvà modi menu caru per fà a stringa unica abbastanza per i vostri scopi. Per esempiu, saria bè di sementà RC4 cù /dev/urandom è poi generà un flussu pseudo-aleatoriu da ellu. Una suluzione più simplice implica una cumminazione di u tempu Unix in una risoluzione di microsecondi più l'ID di u cliente; ùn hè micca cusì sicuru, ma hè probabilmente à u compitu in a maiò parte di i cuntesti.

U tempu chì usemu cum'è una misura di a vita di a chjave hè chjamatu "vita di serratura". Stu valore hè à tempu a quantità di tempu prima chì a serratura hè liberata automaticamente è a quantità di tempu chì un cliente hà da compie una operazione prima chì un altru clientu pò a so volta chjude quella risorsa, senza veramente violà e garanzie di esclusione mutuale. Sta guaranzia hè limitata solu à una certa finestra di u tempu, chì principia da u mumentu chì a serratura hè acquistata.

Allora avemu discututu un bonu modu per acquistà è liberà una serratura. U sistema (se parlemu di un sistema micca distribuitu custituitu da una sola istanza è sempre dispunibule) hè sicuru. Estendemu stu cuncettu à un sistema distribuitu, induve ùn avemu micca tali garanzie.

Algoritmu Redlock

A versione distribuita di l'algoritmu assume chì avemu N maestri Redis. Questi nodi sò completamente indipendenti l'una di l'altru, per quessa, ùn usemu micca a replicazione o qualsiasi altru sistema di coordinazione implicita. Avemu digià cupertu cumu acquistà è liberate in modu sicuru un serratura in una sola istanza. Pigliemu chì l'algoritmu, quandu travaglia cù una sola istanza, aduprà esattamente stu metudu. In i nostri esempi avemu stabilitu N à 5, chì hè un valore raghjone. Cusì, avemu bisognu di utilizà 5 maestri Redis in diverse computer o macchine virtuali per assicurà chì agiscenu largamente indipindentamente l'una di l'altru.

Per acquistà una serratura, u cliente esegue e seguenti operazioni:

  1. Ottiene l'ora attuale in millisecondi.
  2. Tenta sequenzialmente di ottene un serratura in tutti i casi N, utilizendu u listessu nome chjave è valori aleatori in tutti i casi. In u Stage 2, quandu u cliente crea una serratura per esempiu, u cliente usa un ritardu per acquistà chì hè abbastanza cortu cumparatu cù u tempu dopu chì a serratura hè liberata automaticamente. Per esempiu, se a durata di u bloccu hè di 10 seconde, u ritardu puderia esse in u range di ~ 5-50 millisecondi. Questu elimina a situazione in quale u cliente puderia esse bluccatu per un bellu pezzu per pruvà à ghjunghje à un node Redis fallutu: se l'istanza ùn hè micca dispunibile, allora pruvemu di cunnette à un'altra istanza u più prestu pussibule.
  3. Per piglià una serratura, u cliente calcula quantu tempu hè passatu; Per fà questu, sottrae da u valore di u tempu attuale u timestamp chì hè stata ottenuta in u passu 1. Se è solu se u cliente hà sappiutu ottene u serratura nantu à a maiò parte di l'istanze (almenu 3), è u tempu tutale chì hà pigliatu per fà. uttene u serratura, menu di a durata di a serratura, u serratura hè cunsideratu cum'è ottenuta.
  4. Se una serratura hè stata acquistata, a durata di a serratura hè presa per esse a durata di u serratura originale minus u tempu trascorsu calculatu in u passu 3.
  5. Se u cliente ùn riesce à ottene u serratura per una certa ragione (o ùn era micca capace di bluccà N/2 + 1 casi, o a durata di a serratura era negativa), allora pruverà à sbloccare tutte e istanze (ancu quelli chì pensava chì ùn pudia micca bluccà). ).

L'algoritmu hè asincronu?

Stu algoritmu hè basatu annantu à l'assunzione chì, ancu s'ellu ùn ci hè micca un clock sincronizatu nantu à quale tutti i prucessi anu da travaglià, l'ora locale in ogni prucessu scorri sempre à circa u listessu ritmu, è l'errore hè chjucu cumparatu cù u tempu tutale dopu chì u serratura hè. liberatu automaticamente. Questa assunzione hè assai simili à a situazione tipica per l'urdinatori ordinali: ogni computer hà un clock lucale, è generalmente pudemu cuntà u fattu chì a diffarenza di u tempu trà e diverse computer hè chjuca.

À questu puntu, duvemu furmulà più currettamente a nostra regula d'esclusione mutuale: l'esclusione mutuale hè garantita solu s'ellu u cliente chì tene a serratura esce durante u tempu chì u serratura hè validu (stu valore ottenutu in u passu 3), minus un pocu di più tempu (totale uni pochi millisecondi per cumpensà a diffarenza di tempu trà i prucessi).

L'articulu interessante chì segue dice più nantu à tali sistemi chì necessitanu a coordinazione di intervalli di tempu: Leasing: un meccanismo efficiente di tolleranza di difetti per a coerenza di cache di file distribuiti.

Riprova in casu di fallimentu

Quandu un cliente ùn riesce à acquistà una serratura, deve pruvà di novu dopu un ritardu aleatoriu; questu hè fattu per desincronizà parechji clienti chì pruvate d'acquistà una serratura nantu à a stessa risorsa à u stessu tempu (chì pò purtà à una situazione "split-brain" in quale ùn ci sò micca vincitori). Inoltre, più veloce un cliente prova di acquistà un serratura in a maiò parte di l'istanze Redis, più stretta hè a finestra in quale una situazione di split-brain pò accade (è menu hè a necessità di ripetiri). Dunque, idealmente, u cliente deve pruvà à mandà cumandamenti SET à N istanze simultaneamente utilizendu multiplexing.

Vale a pena enfatizà quì quantu hè impurtante per i clienti chì ùn riescenu à acquistà a maiò parte di i chjusi per liberà i chjusi (parzialmente) acquistati, per ùn avè micca aspittà chì a chjave scadrà prima chì a serratura nantu à a risorsa pò esse acquistata di novu. (Ancu se a frammentazione di a rete si trova, è u cliente perde u cuntattu cù l'istanze Redis, allora avete da pagà una penalità di dispunibilità mentre aspetta chì a chjave scade).

Rilascia u serratura

A liberazione di una serratura hè una operazione simplice chì richiede solu di sbloccare tutte l'istanze, indipendentemente da se u cliente pare avè chjusu cù successu una istanza particulare.

Considerazioni di sicurezza

L'algoritmu hè sicuru? Pruvemu d'imagine ciò chì succede in diverse scenarii.

Per principià, supponemu chì u cliente hà sappiutu ottene una serratura in a maiò parte di i casi. Ogni istanza cuntene una chjave cù a stessa vita per tutti. Tuttavia, ognuna di sti chjavi hè stata stallata in un tempu diversu, cusì scaduranu in tempi diffirenti. Ma, se a prima chjave hè stata installata in un tempu micca peggiu chè T1 (u tempu chì avemu sceltu prima di cuntattà u primu servitore), è l'ultima chjave hè stata installata in un tempu micca peggiu chè T2 (u tempu à quale a risposta hè stata ricevuta). da l'ultimu servitore), allora simu cunfidenti chì a prima chjave in u settore chì scade, sopravvive almenu MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT. Tutti l'altri chjavi scaduranu più tardi, cusì pudemu esse sicuru chì tutti i chjavi seranu simultaneamente validi per almenu questu tempu.

Duranti u tempu chì a maiò parte di e chjave restanu valide, un altru cliente ùn serà micca capaci di acquistà a serratura, postu chì l'operazioni N/2+1 SET NX ùn pò micca riesce se e chjavi N/2+1 esistenu digià. Per quessa, una volta una serratura hè stata acquistata, hè impussibile di acquistà novu in u stessu mumentu (questu viulerà a pruprietà di l'exclusione mutuale).
Tuttavia, vulemu assicurà chì parechji clienti chì pruvate d'acquistà una serratura à u stessu tempu ùn ponu micca successu à u stessu tempu.

Se u cliente hà chjusu a maiò parte di l'istanze per circa o più di a durata massima di serratura, cunsidererà a serratura invalida è sbloccarà l'istanze. Dunque, avemu solu piglià in contu u casu in u quali u cliente hà sappiutu à bluccà a maiò parte di i casi in un tempu menu di a data di scadenza. In questu casu, in quantu à l'argumentu sopra, durante u tempu MIN_VALIDITY nisun cliente deve esse capaci di riacquistà a serratura. Per quessa, parechji clienti puderanu chjude N / 2 + 1 istanze in u stessu tempu (chì finiscinu à a fine di a tappa 2) solu quandu u tempu di chjude a maiuranza era più grande di u tempu TTL, chì rende a serratura invalida.

Pudete furnisce una prova formale di sicurità, indicà algoritmi simili esistenti, o truvà un bug in u sopra?

Considerazioni di l'accessibilità

A dispunibilità di u sistema dipende da trè caratteristiche principali:

  1. Rilascia automaticamente i chjusi (cum'è e chjavi scadenu): I chjavi seranu eventualmente dispunibili di novu per esse utilizati per i chjusi.
  2. U fattu chì i clienti di solitu aiutanu à l'altri sguassendu i chjusi quandu a serratura desiderata ùn hè micca stata acquistata, o hè stata acquistata è u travagliu hè finitu; cusì hè prubabile chì ùn avemu micca bisognu di aspittà chì i chjavi scadunu per riacquistà a serratura.
  3. U fattu chì quandu un cliente hà bisognu di ripruvà per acquistà una serratura, aspetta per un tempu comparativamente più longu chì u periodu necessariu per acquistà a maiò parte di serrature. Questu reduce a probabilità di una situazione di split-brain nascendu quandu si compite per risorse.

In ogni casu, ci hè una penalità di dispunibilità uguale à u TTL di i segmenti di a rete, perchè s'ellu ci sò segmenti contigui, a penalità pò esse indefinita. Questu succede ogni volta chì un cliente acquista una serratura è poi strappa à un altru segmentu prima di pudè liberà.

In principiu, datu infiniti segmenti di rete contigui, un sistema pò esse indisponibile per un periudu infinitu di tempu.

Prestazione, failover è fsync

Parechje persone utilizanu Redis perchè anu bisognu di prestazione di u servitore di serratura d'alta prestazione in quantu à a latenza necessaria per acquistà è liberate chjusi, è u nùmeru di operazioni di acquisizione / liberazione chì ponu esse cumplette per seconda. Per risponde à questu requisitu, ci hè una strategia per cumunicà cù i servitori N Redis per riduce a latenza. Questa hè una strategia di multiplexing (o "multiplexing di l'omu poviru", induve u socket hè messu in modu senza bloccu, manda tutti i cumandamenti, è leghje i cumandamenti più tardi, assumendu chì u tempu di andata e ritorno trà u cliente è ogni istanza hè simile) .

Tuttavia, avemu dinù à piglià in contu a cunsiderà assuciata incù u almacenamentu di dati-longu termine s'è no strive à creà un mudellu cù ripresa affidativa da fallimenti.

In fondu, per chjarificà u prublema, supponemu chì cunfiguremu Redis senza almacenamentu di dati à longu andà. U cliente riesce à bluccà 3 di 5 casi. Unu di i casi chì u cliente hà sappiutu di bluccà hè riavviatu, è in questu mumentu ci sò 3 istanze di novu per a stessa risorsa, chì pudemu bluccà, è un altru cliente pò, à u turnu, bluccà l'istanza riavviata, violendu a pruprietà di sicurità chì assume l'esclusività di serrature.

Se attivate i dati avanti (AOF), a situazione hà da migliurà ligeramente. Per esempiu, pudete prumove un servitore mandendu u cumandimu SHUTDOWN è riavvia. Siccomu l'operazioni di scadenza in Redis sò implementate semanticamente in modu chì u tempu cuntinueghja à flussu ancu quandu u servitore hè disattivatu, tutti i nostri bisogni sò bè. Questu hè normale sempre chì un arrestu regulare hè assicuratu. Cosa da fà in casu di mancanza di energia? Se Redis hè cunfiguratu per difettu, cù fsync synchronizing in discu ogni siconda, allora hè pussibule chì dopu à un reiniciu ùn avemu micca a nostra chjave. Teoricamente, se vulemu guarantisci a sicurità di serratura durante ogni riavviu d'istanza, duvemu attivà fsync=always in i paràmetri per u almacenamentu di dati à longu andà. Questu ucciderà cumplettamente u rendiment, finu à u livellu di i sistemi CP chì sò tradizionalmente usati per implementà in modu sicuru i chjusi distribuiti.

Ma a situazione hè megliu di ciò chì pare à prima vista. In principiu, a sicurità di l'algoritmu hè cunsirvatu perchè quandu l'istanza hè riavviata dopu un fallimentu, ùn participa più à alcuna serratura chì hè attualmente attiva.

Per assicurà questu, avemu solu bisognu di assicurà chì, dopu un fallimentu, l'istanza resta indisponibile per un periudu di tempu chì supera ligeramente u TTL massimu chì usemu. In questu modu, aspitteremu finu à a data di scadenza è a liberazione automatica di tutte e chjave chì eranu attive à u mumentu di u fallimentu.

Utilizendu riavvii ritardati, hè in principiu pussibule di ottene a sicurità ancu in l'absenza di qualsiasi persistenza à longu andà in Redis. Nota, però, chì questu pò esse una multa per violazione di l'accessibilità. Per esempiu, se a maiò parte di i casi fallenu, u sistema diventerà indisponibile in u mondu per u TTL (è nisuna risorsa pò esse bluccata durante questu tempu).

Aumentemu a dispunibilità di l'algoritmu: estendemu u bluccatu

Se u travagliu realizatu da i clienti hè custituitu di picculi passi, hè pussibule di riduce a durazione predeterminata di serratura è implementà un mecanismu per allargà i serrature. In principiu, se u cliente hè occupatu in l'informatica è u valore di scadenza di a serratura hè periculosamente bassu, pudete mandà un script Lua à tutti i casi chì estende u TTL di a chjave se a chjave esiste sempre è u so valore hè sempre un valore aleatoriu ottenutu quandu u serratura hè stata acquistata.

Un clientu deve cunsiderà solu una serratura per esse riacquistata s'ellu hà sappiutu di chjude a maiò parte di i casi in u periodu di validità.

True, tecnicamente l'algoritmu ùn cambia micca, cusì u numeru massimu di tentativi ripetuti per acquistà chjusi deve esse limitatu, altrimente e pruprietà di l'accessibilità seranu violate.

Source: www.habr.com

Add a comment