GH GambleHub

Verteilte Sperren

1) Warum (und wann) verteilte Sperren benötigt werden

Distributed Lock ist ein Mechanismus, der sicherstellt, dass sich ein kritischer Abschnitt zwischen mehreren Clusterknoten gegenseitig ausschließt. Typische Aufgaben:
  • Leadership (Leader-Auswahl) für die Hintergrundaufgabe/den Sheduler.
  • Beschränkung eines einzelnen Ausführenden auf eine freigegebene Ressource (Verschieben von Dateien, Migration eines Schemas, exklusiver Zahlungsschritt).
  • Sequentielle Bearbeitung des Aggregats (Wallet/Order), wenn sonst keine Idempotenz/Ordnung erreicht werden kann.
Wenn es besser ist, das Schloss nicht zu benutzen:
  • Wenn Sie ein idempotentes Upsert, ein CAS (compare-and-set) oder eine Warteschlange nach Schlüssel (per-key ordering) erstellen können.
  • Wenn die Ressource Switched Operations (CRDT, Zähler) zulässt.
  • Wenn das Problem durch eine Transaktion im selben Speicher gelöst wird.

2) Bedrohungsmodell und Eigenschaften

Fehler und Schwierigkeiten:
  • Netzwerk: Latenz, Partition, Paketverlust.
  • Die Abläufe: GC-Pause, Stop-the-World, Crash nach der Einnahme der Burg.
  • Zeit: Uhrendrift und Offset brechen TTL-Ansätze.
  • Re-Ownership: Der „Zombie“ -Prozess nach dem Netzwerk könnte denken, dass er immer noch das Schloss besitzt.
Gewünschte Eigenschaften:
  • Sicherheit: nicht mehr als ein gültiger Besitzer (Sicherheit).
  • Überlebensfähigkeit: Das Schloss wird freigegeben, wenn der Besitzer ausfällt (Lebendigkeit).
  • Gerechtigkeit: Es gibt keinen Hunger.
  • Unabhängigkeit von der Uhr: Die Korrektheit hängt nicht von der Wanduhr ab (oder wird durch Fencing-Token kompensiert).

3) Hauptmodelle

3. 1 Lease (Mietschloss)

Das Schloss wird mit TTL ausgegeben. Der Eigentümer ist verpflichtet, es vor Ablauf zu verlängern (heartbeat/keepalive).

Vorteile: Selbstbestimmt beim Crash.
Risiken: Wenn der Eigentümer „aufgehängt“ ist und weiterarbeitet, aber die Verlängerung verloren hat, kann Doppelbesitz entstehen.

3. 2 Fencing Token (Zauntoken)

Bei jedem erfolgreichen Greifer wird eine monoton wachsende Zahl ausgegeben. Die Verbraucher der Ressource (DB, Warteschlange, Dateispeicher) überprüfen das Token und lehnen Operationen mit der alten Nummer ab.
Dies ist bei TTL/Lease- und Netzwerktrennungen extrem wichtig - schützt vor dem „alten“ Besitzer.

3. 3 Quorumschlösser (CP-Systeme)

Verwenden Sie einen verteilten Konsens (Raft/Paxos; etcd/ZooKeeper/Consul), ist der Eintrag mit einem Konsenslog verknüpft → bei den meisten Knoten gibt es keinen Split-Break.

Plus: starke Sicherheitsgarantien.
Nachteil: Empfindlichkeit gegenüber Quorum (wenn es verloren geht, ist die Überlebensfähigkeit lahm).

3. 4 AP-Schlösser (In-Memory/Cache + Replikation)

Zum Beispiel ein Redis-Cluster. Hohe Verfügbarkeit und Geschwindigkeit, aber keine strengen Sicherheitsgarantien bei Netzwerktrennungen. Erfordern fencing auf der Seite des Sync.

4) Plattformen und Muster

4. 1 etcd/ZooKeeper/Consul (empfohlen für starke Schlösser)

Ephemere Knoten (ZK) oder Sitzungen/leases (etcd): Der Schlüssel existiert, während die Sitzung live ist.
Sitzung keepalive; Verlust des Quorums → die Sitzung läuft ab → das Schloss wird freigegeben.
Sequenzknoten (ZK 'EPHEMERAL _ SEQUENTIAL') für die Warteschlange → Fairness.

Skizze auf 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 (ordentlich)

Der Klassiker ist der 'SET Schlüsselwert NX PX ttl'.

Die Probleme:
  • Replikation/Failover können gleichzeitige Besitzer zulassen.
  • Redlock von mehreren Instanzen reduziert das Risiko, beseitigt aber nicht; ist in Umgebungen mit unzuverlässigem Netzwerk umstritten.

Es ist sicherer, Redis als schnelle Koordinationsschicht anzuwenden, aber immer das Fencing-Token in der Zielressource zu ergänzen.

Beispiel (Lua-unlock):
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 OBD-Schlösser

PostgreSQL advisory locks: lok innerhalb des Postgres-Clusters (Prozess/Sitzung).
Es ist gut, wenn alle kritischen Abschnitte ohnehin in einer DB sind.

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

4. 4 Datei/Cloud „Schlösser“

S3/GCS + Objektmetadaten mit den Bedingungen von „If-Match“ (ETag) → im Wesentlichen CAS.
Geeignet für Backups/Migrationen.

5) Entwurf des sicheren Schlosses

5. 1 Identität des Eigentümers

Speichern Sie' owner _ id'(Knoten # Prozess # pid # start _ time) + ein zufälliges Token zum Abgleich bei Unlock.
Wiederholtes Entsperren sollte das Schloss eines anderen nicht entfernen.

5. 2 TTL und Verlängerung

TTL <T_fail_detect (Time to Detection of Failure) und ≥ p99 Betrieb des kritischen Abschnitts × Reserve.
Verlängerung - periodisch (z.B. alle' TTL/3'), mit deadline.

5. 3 Fencing Token auf Blau

Der Abschnitt, der die externe Ressource ändert, muss' fencing _ token 'übergeben.

Synk (DB/Cache/Vault) speichert 'last _ token' und lehnt kleinere ab:
sql
UPDATE wallet
SET balance = balance +:delta, last_token =:token
WHERE id =:id AND:token > last_token;

5. 4 Warteliste und Fairness

In ZK - 'EPHEMERAL _ SEQUENTIAL' und Beobachter: der Kunde wartet auf die Freigabe des nächsten Vorgängers.
In etcd - Schlüssel mit Revision/Versionierung; Priorität durch 'mod _ revision'.

5. 5 Verhalten bei Split-Brain

CP-Ansatz: Ohne Quorum kann man das Schloss nicht nehmen - besser stehen als die Sicherheit brechen.
AP-Ansatz: Fortschritte auf geteilten Inseln sind erlaubt → Fencing ist erforderlich.

6) Führung (Leader-Wahl)

In etcd/ZK - „leader“ ist dies ein exklusiver Epemer-Schlüssel; Die anderen haben die Änderungen unterschrieben.
Der Führer schreibt heartbeats; Verlust - Wiederwahl.
Alle Operationen des Führers begleiten fencing token (Epoche/Revision Nummer).

7) Fehler und deren Handhabung

Der Kunde nahm das Schloss, aber der Absturz vor der Arbeit → Normen, niemand wird verletzt; Die TTL/Session wird freigegeben.

Das Schloss ist mitten im Werk ausgelaufen:
  • Obligatorischer Watchdog: Wenn die Verlängerung fehlschlägt, unterbrechen Sie den kritischen Abschnitt und rollen Sie zurück/kompensieren Sie.
  • Kein „Nachkriegen“: Ohne Schloss kann der kritische Abschnitt nicht weitergeführt werden.

Eine lange Pause (GC/stop-the-world) → die Verlängerung kam nicht zustande, ein anderer nahm das Schloss. Der Workflow muss den Verlust des Eigentums (keepalive-Kanal) erkennen und unterbrechen.

8) Dedloki, Prioritäten und Inversion

Dedloki sind in einer verteilten Welt selten (ein Schloss ist normalerweise eins), aber wenn es mehrere Schlösser gibt, halten Sie sich an eine einzige Reihenfolge der Einnahme (Lock Ordering).
Umkehrung der Prioritäten: Der Besitzer mit niedriger Priorität behält die Ressource, während die Besitzer mit hoher Priorität warten. Lösungen: TTL-Grenzen, Präemption (wenn das Geschäft erlaubt), Ressourcen-Sharding.
Fasten: Verwenden Sie Warteschlangen (ZK-Unterreihenknoten) für Fairness.

9) Beobachtbarkeit

Metriken:
  • `lock_acquire_total{status=ok|timeout|error}`
  • `lock_hold_seconds{p50,p95,p99}`
  • 'fencing _ token _ value' (Monotonie)
  • `lease_renew_fail_total`
  • „split _ brain _ prevented _ total“ (Wie viele Versuche wurden aufgrund des fehlenden Quorums abgelehnt)
  • `preemptions_total`, `wait_queue_len`
Protokolle/Tracing:
  • `lock_name`, `owner_id`, `token`, `ttl`, `attempt`, `wait_time_ms`, `path` (для ZK), `mod_revision` (etcd).
  • Spans „acquire → critical section → release“ mit dem Ergebnis.
Alerts:
  • Wachstum von 'lease _ renew _ fail _ total'.
  • `lock_hold_seconds{p99}` > SLO.
  • „Orphan“ Schlösser (ohne Herzschlag).
  • Aufgeblähte Wartelisten.

10) Praktische Beispiele

10. 1 Sicheres Redis-Schloss mit Fencing (Pseudo)

1. Wir speichern den Token-Zähler in einem zuverlässigen Stor (z. B. Postgres/etcd).
2. Wenn 'SET NX PX' erfolgreich ist, lesen/inkrementieren wir den Token und machen alle Änderungen an der Ressource mit der Überprüfung des Tokens im DB/Service.

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 (Go)

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 Führung in ZK (Java, Curator)

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

10. 4 Postgres Advisory Lock mit Deadline (SQL + App)

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

11) Test-Playbooks (Spieltage)

Verlust des Quorums: Deaktivieren Sie 1-2 etcd-Knoten → der Versuch, das Schloss zu nehmen, sollte nicht bestehen.
GC-Pause/Stop-the-World: künstlich den Fluss des Besitzers verzögern → überprüfen, ob der Watchdog den Betrieb unterbricht.
Split-Brain: Emulation der Netztrennung zwischen dem Besitzer und den Seiten des Schlosses → der neue Besitzer erhält einen höheren Fencing-Token, der alte wird von einem blauen abgelehnt.
Clock skew/drift: Nehmen Sie die Uhr vom Besitzer (für Redis/lease) → stellen Sie sicher, dass die Korrektheit durch Token/Kontrollen gewährleistet ist.
Crash before release: Drop-Prozess → das Schloss wird durch TTL/Sitzung freigegeben.

12) Anti-Muster

Sauberes TTL-Schloss ohne Fencing beim Zugriff auf eine externe Ressource.
Verlassen Sie sich auf die lokale Zeit für die Richtigkeit (ohne HLC/fencing).
Verteilung der Schlösser durch einen Redis-Master in einer Umgebung mit einem Failover und ohne Bestätigung der Repliken.
Endless Critical Section (TTL „seit Jahrhunderten“).
Entfernen eines „fremden“ Schlosses ohne Abgleich 'owner _ id '/token.
Das Fehlen von Backoff + Jitter → ein „Sturm“ von Versuchen.
Ein einziges globales Schloss „für alles“ - eine Tasche von Konflikten; Sharding nach Schlüssel ist besser.

13) Checkliste Umsetzung

  • Die Art der Ressource ist definiert und ob mit CAS/Queue/Idempotence umgegangen werden kann.
  • Mechanismus gewählt: etcd/ZK/Consul für CP; Redis/Cache - nur mit Fencing.
  • Implementiert: 'owner _ id', TTL + Verlängerung, Watchdog, korrektes Freischalten.
  • Die externe Ressource überprüft den fencing token (Monotonie).
  • Es gibt eine Strategie für Führung und Versagen.
  • Metriken, Alerts, Token- und Revisionsprotokollierung konfiguriert.
  • Backoff + Jitter und Timeouts auf Acquire sind vorgesehen.
  • Veranstaltete Spieltage: Quorum, Split-Brain, GC-Pausen, Clock Skew.
  • Dokumentation der Reihenfolge, in der mehrere Schlösser gezogen werden (falls erforderlich).
  • Abbauplan (Brownout): Was tun, wenn das Schloss nicht zugänglich ist.

14) FAQ

F: Reicht das Redis-Schloss' SET NX PX'?
A: Nur wenn die Ressource überprüft fencing token. Ansonsten sind mit der Netzteilung zwei Eigentümer möglich.

Q: Was wählen Sie „Standard“?
A: Für strenge Garantien - etcd/ZooKeeper/Consul (CP). Für leichte Aufgaben innerhalb einer DB - advisory locks Postgres. Redis ist nur mit fencing.

F: Welche TTL setzen?
A: „TTL ≥ p99 der Dauer des kritischen Abschnitts × 2“ und kurz genug, um die „Zombies“ schnell zu löschen. Erneuerung - alle „TTL/3“.

F: Wie vermeide ich Hunger?
A: Warteschlange in der Reihenfolge (ZK sequential) oder fairness-Algorithmus; Versuchsbegrenzung und faire Planung.

F: Ist eine Zeitsynchronisation erforderlich?
A: Für die Richtigkeit - nein (verwenden Sie fencing). Für die betriebliche Vorhersagbarkeit - ja (NTP/PTP), aber verlassen Sie sich nicht auf die Wanduhr in der Schlosslogik.

15) Ergebnisse

Zuverlässige verteilte Sperren basieren auf Quorum-Sektoren (etcd/ZK/Consul) mit lease + keepalive und werden notwendigerweise durch ein Fencing-Token auf der Ebene der zu ändernden Ressource ergänzt. Alle TTL/Redis-Ansätze ohne Zaun sind das Risiko eines Split-Break. Denken Sie zuerst an Kausalität und Idempotenz, nutzen Sie Sperren, wo es ohne sie nicht geht, messen, testen Sie Ausfallmodi - und Ihre „kritischen Abschnitte“ bleiben nur im Sinne und nicht in der Anzahl der Vorfälle kritisch.

Contact

Kontakt aufnehmen

Kontaktieren Sie uns bei Fragen oder Support.Wir helfen Ihnen jederzeit gerne!

Integration starten

Email ist erforderlich. Telegram oder WhatsApp – optional.

Ihr Name optional
Email optional
Betreff optional
Nachricht optional
Telegram optional
@
Wenn Sie Telegram angeben – antworten wir zusätzlich dort.
WhatsApp optional
Format: +Ländercode und Nummer (z. B. +49XXXXXXXXX).

Mit dem Klicken des Buttons stimmen Sie der Datenverarbeitung zu.