GH GambleHub

Încuietori distribuite

1) De ce (și când) sunt necesare încuietori distribuite

Blocarea distribuită este un mecanism care garantează excluderea reciprocă pentru o secțiune critică între mai multe noduri de cluster. Sarcini tipice:
  • Alegerea liderului pentru sarcina de fundal/shader.
  • Restricționarea unui singur interpret asupra unei resurse partajate (mișcarea fișierelor, migrarea schemelor, etapa de plată exclusivă).
  • Procesarea secvențială a agregatului (portofel/comandă) dacă este imposibil să se obțină idempotența/ordonarea în alt mod.
Când este mai bine să nu utilizați blocarea:
  • Dacă puteți face un upsert idempotent, CAS (comparați-și-set) sau comanda pe cheie.
  • Dacă resursa permite operațiuni comutative (CRDT, contoare).
  • Dacă problema este rezolvată printr-o tranzacție într-un singur magazin.

2) Modelul și proprietățile amenințării

Eșecuri și complicații:
  • Rețea: întârzieri, partiții, pierderi de pachete.
  • Procese: pauză GC, stop-the-world, accident după captura de blocare.
  • Timp: Ceas derivă și deplasare pauză TTL se apropie.
  • Repossession: Procesul „zombie” după net poate crede că încă deține castelul.
Proprietăți dorite:
  • Siguranță: nu mai mult de un proprietar valabil (siguranță).
  • Supraviețuirea: blocarea este eliberată atunci când proprietarul nu reușește (viață).
  • Justiție: Nu există post.
  • Independența ceasului: corectitudinea nu depinde de ceasul de perete (sau compensată de jetoanele de scrimă).

3) Principalele modele

3. 1 Inchiriere (inchiriere de inchiriere)

Încuietoarea este emisă cu TTL. Proprietarul este obligat să o reînnoiască înainte de expirare (bătăi ale inimii/păstrare).

Pro: Crashing auto-absorbție.
Riscuri: dacă proprietarul este „blocat” și continuă să lucreze, dar a pierdut prelungirea, poate apărea o proprietate dublă.

3. 2 jeton de scrimă

Cu fiecare captură reușită, se emite un număr în creștere monotonă. Consumatorii de resurse (bază de date, coadă, stocare fișiere) verifica token și respinge operațiunile cu numărul vechi.
Acest lucru este extrem de important pentru TTL/leasing și partiții de rețea - protejează împotriva proprietarului „vechi”.

3. 3 Încuietori de cvorum (sisteme CP)

Se utilizează consensul distribuit (Raft/Paxos; etcd/ZooKeeper/Consul), înregistrarea este asociată cu un jurnal de consens → nu există creier divizat cu majoritatea nodurilor.

Plus: garanții puternice de securitate.
Minus: sensibilitate la cvorum (atunci când este pierdut, supraviețuirea este lame).

3. 4 încuietori AP (memorie/memorie cache + replicare)

De exemplu, un cluster Redis. Disponibilitate și viteză ridicată, dar fără garanții puternice de securitate pentru partițiile de rețea. Necesită garduri pe partea laterală a vânătăii.

4) Platforme și modele

4. 1 etcd/ZooKeeper/Consul (recomandat pentru încuietori puternice)

Noduri efemere (ZK) sau sesiuni/leasing (etcd): cheia există în timp ce sesiunea este în viață.
Keepalive sesiune; pierderea cvorumului → sesiunea expiră → blocarea este eliberată.
Nodurile de secvență (ZK 'EPHEMERAL _ SECVENTIAL') pentru coada de așteptare → corectă.

Schiță pe etcd (Go):
go cli, _:= clientv3. New(...)
lease, _:= cli. Grant(ctx, 10)            // 10s lease sess, _:= concurrency. NewSession(cli, concurrency. WithLease(lease. ID))
m:= concurrency. NewMutex(sess, "/locks/orders/42")
if err:= m. Lock(ctx); err!= nil { / handle / }
defer m. Unlock(ctx)

4. 2 Redis (îngrijit)

Clasic - "SET valoare cheie NX PX ttl'.

Probleme:
  • Replicarea/feilover poate permite proprietarilor simultani.
  • Redlock din mai multe instanțe reduce riscul, dar nu elimină; este controversat în medii cu o rețea nesigură.

Este mai sigur de a utiliza Redis ca un strat de coordonare rapid, dar întotdeauna completa jetonul de scrimă în resursa țintă.

Exemplu (Lua-deblocare):
lua
-- release only if value matches if redis. call("GET", KEYS[1]) == ARGV[1] then return redis. call("DEL", KEYS[1])
else return 0 end

4. 3 încuietori DB

Încuietori de consiliere PostgreSQL: blocare în cadrul clusterului Postgres (proces/sesiune).
Este bine atunci când toate secțiunile critice sunt deja în aceeași bază de date.

SQL:
sql
SELECT pg_try_advisory_lock(42); -- take
SELECT pg_advisory_unlock(42); -- let go

4. 4 Încuietori de fișiere/nori

S3/GCS + blocarea metadatelor obiectului cu condițiile 'If-Match' (ETag) → în esență CAS.
Potrivit pentru copii de rezervă/migrații.

5) Design de blocare de siguranță

5. 1 Identitatea proprietarului

Store 'owner _ id' (# process # pid # start _ time nod) + token aleatoriu pentru verificarea deblocării.
Deblocarea repetată nu trebuie să elimine blocarea altcuiva.

5. 2 TTL și extensie

TTL <T_fail_detect (timpul de detectare a defecțiunilor) și p99 ≥ de funcționare a secțiunii critice × rezervă.
Reînnoire - periodic (de exemplu, fiecare „TTL/3”), cu termen limită.

5. 3 Jeton de scrimă pe o vânătaie

Secțiunea care modifică resursa externă trebuie să treacă de 'garding _ token'.

Chiuveta (DB/cache/storage) stochează ultimele _ token 'și le respinge pe cele mai mici:
sql
UPDATE wallet
SET balance = balance +:delta, last_token =:token
WHERE id =:id AND:token > last_token;

5. 4 Coadă de așteptare și justiție

În ZK - 'EPHEMERAL _ SECVENTIAL' și observatori: clientul așteaptă eliberarea celui mai apropiat predecesor.
În etcd - chei cu revizie/versioning; comanda prin 'mod _ revision'.

5. 5 Comportamentul creierului divizat

Abordarea CP: fără cvorum, nu puteți lua o blocare - este mai bine să stați în picioare decât să rupeți siguranța.
Abordare AP: progresul este permis în insulele divizate → este necesară împrejmuirea.

6) Alegerea liderului

În etcd/ZK, „liderul” este o cheie epemerică exclusivă; restul sunt înscrise pentru schimbări.
Liderul scrie bătăi de inimă; pierdere - realegere.
Însoțiți toate operațiunile lider cu un jeton de scrimă (era/numărul de revizuire).

7) Erori și prelucrarea acestora

Clientul a luat de blocare, dar accident pentru a lucra → norma, nimeni nu va suferi; TTL/sesiune va fi lansat.

Blocarea a expirat în mijlocul muncii:
  • Câine de pază obligatoriu: dacă extensia nu reușește, întrerupeți secțiunea critică și rostogoliți/compensați.
  • Nu „termina mai târziu”: fără o blocare, secțiunea critică nu poate fi continuată.

O pauză lungă (GC/stop-the-world) → extensia nu sa întâmplat, cealaltă a luat blocarea. Fluxul de lucru trebuie să detecteze pierderea proprietății (canal keepalive) și să anuleze.

8) Dedloki, priorități și inversiune

Dedloki într-o lume distribuită sunt rare (există, de obicei, un castel), dar dacă există mai multe castele, aderă la un singur ordin de a lua (ordonare de blocare).
Inversarea priorităților: Un proprietar cu prioritate redusă deține o resursă în timp ce cei cu prioritate ridicată așteaptă. Soluții: limite TTL, preempțiune (dacă afacerea permite), împărțirea resursei.
Post: Utilizați cozi de așteptare (noduri ZK-sub-ordine) pentru corectitudine.

9) Observabilitate

Măsurători:
  • 'lock _ acquire _ total {status = ok' timeout 'error}'
  • 'lock _ hold _ seconds {p50, p95, p99}'
  • 'fencing _ token _ value' (monotonie)
  • 'lease _ renew _ fail _ total'
  • 'split _ brain _ prevenit _ total' (număr de încercări negate din cauza lipsei de cvorum)
  • 'preemptions _ total', 'wait _ queue _ len'
Busteni/vectorizare:
  • 'lock _ name', 'owner _ id',' token ',' ttl', 'încercare', 'wait _ time _ ms',' path '(для ZK),' mod _ revision '(etcd).
  • „dobândi → secțiune critică → eliberare” se întinde cu rezultatul.
Alerte:
  • Growth 'lease _ renew _ fail _ total'.
  • 'lock _ hold _ seconds {p99}'> SLO.
  • Încuietori „orfane” (fără bătăi de inimă).
  • Liste de aşteptare umflate.

10) Studii de caz

10. 1 Blocare sigură Redis cu scrimă (pseudo)

1. Stocăm contorul de token într-un magazin fiabil (de exemplu, Postgres/etcd).
2. Dacă „SET NX PX” are succes, citim/creștem jetonul și facem toate modificările la resursă cu tokenul verificat în baza de date/serviciu.

python acquire token = db. next_token ("locks/orders/42") # monotone ok = redis. set("locks:orders:42", owner, nx=True, px=ttl_ms)
if not ok:
raise Busy()

critical op guarded by token db. exec("UPDATE orders SET... WHERE id=:id AND:token > last_token",...)
release (compare owner)

10. 2 etcd Mutex + watchdog (Du-te)

go ctx, cancel:= context. WithCancel(context. Background())
sess, _:= concurrency. NewSession(cli, concurrency. WithTTL(10))
m:= concurrency. NewMutex(sess, "/locks/job/cleanup")
if err:= m. Lock(ctx); err!= nil { /... / }

// Watchdog go func() {
<-sess. Done ()//loss of session/quorum cancel ()//stop working
}()

doCritical (ctx )//must respond to ctx. Done()
_ = m. Unlock(context. Background())
_ = sess. Close()

10. 3 Leadership în ZK (Java, Curator)

java
LeaderSelector selector = new LeaderSelector(client, "/leaders/cron", listener);
selector. autoRequeue();
selector. start(); // listener. enterLeadership() с try-finally и heartbeat

10. 4 Postgres consultativ de blocare cu termen limită (SQL + app)

sql
SELECT pg_try_advisory_lock(128765); -- attempt without blocking
-- if false --> return via backoff + jitter

11) Cărți de joc de testare (Zilele jocului)

Pierderea cvorumului: dezactivați nodurile 1-2 etcd → încercarea de a lua blocarea nu trebuie să treacă.
GC-pauză/stop-the-world: întârzie artificial fluxul proprietarului → verificați dacă câinele de pază întrerupe lucrul.
Split-creier: emularea separării rețelei între proprietar și partea laterală a castelului → noul proprietar primește un jeton de scrimă mai mare, cel vechi este respins de albastru.
Ceas înclinare/derivă: ia ceasul departe de proprietar (pentru Redis/lease) → asigurați-vă că corectitudinea este asigurată de jetoane/cecuri.
Crash înainte de eliberare: crash proces → blocare este eliberat prin TTL/sesiune.

12) Anti-modele

Curățați blocarea TTL fără garduri atunci când accesați o resursă externă.
Bazați-vă pe ora locală pentru corectitudine (fără HLC/scrimă).
Distribuția încuietorilor printr-un singur maestru Redis într-un mediu cu feilover și fără confirmarea replicilor.
Secțiune critică infinită (TTL „pentru vârste”).
Scoateți blocarea altcuiva fără a verifica 'owner _ id'/token.
Lipsa de backoff + jitter → furtună de încercări.
O singură blocare globală „pentru orice” - un sac de conflicte; cheie de sharding este mai bine.

13) Lista de verificare a implementării

  • Tipul de resursă definit și poate fi eliberat CAS/coadă/idempotență.
  • Mecanism selectat: etcd/ZK/Consul pentru CP; Redis/cache - numai cu garduri.
  • Implementat: 'owner _ id', extensie TTL +, watchdog, deblocare corectă.
  • Resursa externă verifică jetonul de scrimă (monotonie).
  • Există o strategie de conducere și eșec.
  • Măsurători configurate, alerte, token-uri de logare și revizuiri.
  • Backoff + jitter și de a dobândi timeout sunt furnizate.
  • Zile de joc a avut loc: cvorum, split-creier, pauze GC, ceas înclinare.
  • Documentarea procedurii de luare a mai multor încuietori (dacă este necesar).
  • plan brownout - ce să faceți atunci când blocarea nu este disponibilă.

14) ÎNTREBĂRI FRECVENTE

Î: Este suficient de blocare „SET NX PX” Redis?
R: Numai dacă resursa verifică jetonul de scrimă. În caz contrar, cu partiționarea rețelei, sunt posibili doi proprietari.

Î: Ce să alegeți „în mod implicit”?
R: Pentru garanții stricte - etcd/ZooKeeper/Consul (CP). Pentru sarcini ușoare într-o bază de date - consultativ blochează Postgres. Redis - numai cu scrimă.

Î: Ce TTL pentru a pune?
R: "TTL ≥ p99 durata secțiunii critice × 2" și suficient de scurt pentru a șterge rapid "zombi. "Reînnoire - fiecare 'TTL/3'.

Î: Cum să evitați postul?
R: Coadă de așteptare în ordine (secvențial ZK) sau algoritm de corectitudine; limita încercărilor și planificarea corectă.

Î: Am nevoie de sincronizarea timpului?
R: Pentru corectitudine - nu (folosiți garduri). Pentru predictibilitate operațională, da (NTP/PTP), dar nu se bazează pe ceas de perete pentru logica de blocare.

15) Totaluri

Încuietori fiabile distribuite sunt construite pe niveluri de cvorum (etcd/ZK/Consul) cu leasing + keepalive, și sunt în mod necesar completate de garduri token la nivelul resursei schimbate. Orice TTL/Redis abordări fără garduri sunt un risc split-creier. Gândiți-vă mai întâi la cauzalitate și idempotență, utilizați încuietori unde este imposibil fără ele, măsurați, testați modurile de eșec - iar „secțiunile critice” vor rămâne critice numai în sens, nu și în numărul de incidente.

Contact

Contactați-ne

Scrieți-ne pentru orice întrebare sau solicitare de suport.Suntem mereu gata să ajutăm!

Pornește integrarea

Email-ul este obligatoriu. Telegram sau WhatsApp sunt opționale.

Numele dumneavoastră opțional
Email opțional
Subiect opțional
Mesaj opțional
Telegram opțional
@
Dacă indicați Telegram — vă vom răspunde și acolo, pe lângă Email.
WhatsApp opțional
Format: cod de țară și număr (de exemplu, +40XXXXXXXXX).

Apăsând butonul, sunteți de acord cu prelucrarea datelor dumneavoastră.