Das Problem
Warum nicht einfach stark konsistent?
Beispiel: Banküberweisung
Node A (EU): Konto = 100 €
Node B (US): Konto = 100 € (Replik)
Client schreibt auf Node A:
Konto = Konto - 50 € → 50 €
Client liest sofort von Node B:
Konto = 100 € ← veralteter Wert!
Frage: Wann ist Node B synchron?
→ Sofort? (starke Konsistenz)
→ Irgendwann? (Eventual Consistency)
→ Nur bei Quorum? (Tunable Consistency)
Drei Garantien – nur zwei gleichzeitig möglich
Konsequenz
Netzwerkpartition tritt auf
Node A ──✗── Node B
↑ ↑
Client 1 Client 2
CP: Node B lehnt Reads ab
bis Partition geheilt
→ Fehler, aber konsistent
AP: Node B antwortet mit
möglicherweise veraltetem Wert
→ verfügbar, aber inkonsistent
Kritik am CAP-Theorem
Kategorien
| System | Partition | Normal |
|---|---|---|
| Cassandra | PA | EL |
| DynamoDB | PA | EL |
| Spanner | PC | EC |
| HBase | PC | EC |
| MongoDB | PA | EC |
| ZooKeeper | PC | EC |
ACID – relationale Datenbanken
BASE – NoSQL-Systeme
Fazit
Von stark nach schwach (abnehmende Garantien, zunehmende Verfügbarkeit)
| Modell | Garantie | Latenz | Systeme |
|---|---|---|---|
| Linearisierbarkeit | Reads sehen zuletzt geschriebenen Wert, global geordnet | hoch | Spanner, etcd, Zookeeper |
| Sequentielle Konsistenz | Alle sehen gleiche Reihenfolge, aber nicht Echtzeit | mittel | viele ACID-Systeme |
| Kausale Konsistenz | Kausal abhängige Ops in richtiger Reihenfolge sichtbar | mittel | MongoDB (causal sessions) |
| Read-your-writes | Ein Client sieht eigene Writes | niedrig | DynamoDB, Cassandra (session) |
| Eventual Consistency | Irgendwann konsistent – keine Zeitgarantie | minimal | Cassandra ONE, DNS |
Umsetzung
Beispiel
Zeit →
Client A: Write(x=1) ────────────────▶ OK
Client B: Read(x) ────────▶ 1 ✓
Linearisierbar:
Read nach abgeschlossenem Write
muss neuen Wert liefern
Nicht linearisierbar:
Client A: Write(x=1) ──────────────▶ OK
Client B: Read(x) ──────▶ 0 ✗
(Write abgeschlossen, aber 0 gelesen)
Systeme
CONSISTENCY ALLUnterschied zur Linearisierbarkeit
Beispiel: sequentiell konsistent, aber nicht linearisierbar
Prozess P1: Write(x=1)
Prozess P2: Write(x=2)
Prozess P3 sieht: x=1, dann x=2 ✓
Prozess P4 sieht: x=1, dann x=2 ✓
→ alle sehen gleiche Reihenfolge ✓
Aber: P2 schrieb x=2 vor P1 (Wanduhr)
→ Reihenfolge entspricht nicht Realzeit
→ nicht linearisierbar, aber
sequentiell konsistent ✓
Happens-Before (→)
Beispiel
P1: Post("Frage?") Write(x=1)
P2: Read(x=1) → Reply("Antwort!")
P3: ?
Kausal konsistent:
P3 sieht erst "Frage?", dann "Antwort!"
(weil Reply kausal von Post abhängt) ✓
Nicht kausal konsistent:
P3 sieht erst "Antwort!", dann "Frage?"
→ Ursache kommt nach Wirkung ✗
Implementierung
Vorteile
Herausforderungen
Beispiel: DNS
DNS-Eintrag geändert:
example.com → neue IP
Nameserver A: kennt neue IP sofort
Nameserver B: liefert noch alte IP
Nameserver C: liefert noch alte IP
Nach TTL (z.B. 300 s):
alle Nameserver: neue IP ✓
→ Eventual Consistency
Cassandra Beispiel
-- Konsistenzlevel ONE:
-- antwortet nach 1 Replica
-- möglicherweise veraltet
CONSISTENCY ONE;
SELECT * FROM sensor_data
WHERE id = 's-001';
Vier Session-Guarantees (Terry et al. 1994)
Read-your-writes (häufigste Anforderung)
Client schreibt: Profil-Update
Client liest sofort: eigenes Profil
Ohne RYW:
→ Client sieht altes Profil ✗
→ frustrierend!
Mit RYW:
→ Client wird immer zu Replica
geleitet, die seinen Write kennt ✓
Implementierung:
- Sticky Session (immer gleicher Node)
- Session Token mit Versions-Info
Monotonic Reads
Read 1: Konto = 150 €
Read 2: Konto = 100 € ✗ (älter!)
Mit MR: nie rückwärts in der Zeit
Starke Konsistenz: W + R > N
Schwache Konsistenz: W + R ≤ N
Konfigurationen (N = 3)
| W | R | W+R | Konsistenz |
|---|---|---|---|
| 3 | 1 | 4 > 3 | stark (ALL/ONE) |
| 2 | 2 | 4 > 3 | stark (QUORUM) |
| 1 | 3 | 4 > 3 | stark (ONE/ALL) |
| 1 | 1 | 2 ≤ 3 | eventual |
| 1 | 2 | 3 ≤ 3 | eventual |
Tradeoff
Vergleich
Beispiel (3 Knoten: A, B, C)
Initial: A=[0,0,0] B=[0,0,0] C=[0,0,0]
A schreibt x=1:
A=[1,0,0] → sendet an B
B empfängt, schreibt x=2:
B=max([0,0,0],[1,0,0])+1B
B=[1,1,0] → sendet an C
C empfängt, schreibt x=3:
C=[1,1,1]
Nebenläufig (Konflikt):
A schreibt x=4: A=[2,0,0]
B schreibt x=5: B=[1,2,0]
→ [2,0,0] und [1,2,0] unvergleichbar
→ Konflikt! Auflösung notwendig
Wann entstehen Konflikte?
Auflösungsstrategien
Last-Write-Wins (LWW)
Node A: x = "Alice" ts=1000
Node B: x = "Bob" ts=1001
Sync: ts=1001 gewinnt → x = "Bob"
"Alice" geht verloren!
Problem: Clock Skew (Uhren laufen unterschiedlich)
→ ts=1000 auf A könnte real später sein
→ NTP hilft, garantiert aber nichts
Multi-Version (Dynamo-Stil)
Shopping Cart:
v1: ["Buch"] VC=[1,0]
v2: ["Buch","Stift"] VC=[1,1]
v3: ["Buch","Heft"] VC=[2,0]
v2 und v3 nebenläufig → beide behalten
Client sieht beide → wählt Vereinigung:
["Buch","Stift","Heft"]
Beispiele
G-Counter (Grow-only Counter)
Node A zählt: +2 → A=[2, 0, 0]
Node B zählt: +3 → B=[0, 3, 0]
Node C zählt: +1 → C=[0, 0, 1]
Merge (komponentenweises Maximum):
Max([2,0,0],[0,3,0],[0,0,1])
= [2, 3, 1]
Gesamtwert = Summe = 6
→ kein Konflikt, kein Koordinationsbedarf
Einsatz
Synchrone Replikation
Asynchrone Replikation
Konsensbasierte Replikation (Raft/Paxos)
Vergleich
| Protokoll | Konsistenz | Latenz | Verlust |
|---|---|---|---|
| Synchron | stark | hoch | kein |
| Asynchron | eventual | niedrig | möglich |
| Raft/Paxos | stark | mittel | kein |
Cassandra – Tunable Consistency
Google Spanner – Externe Konsistenz
ZooKeeper – Sequentielle Konsistenz
sync() erzwingt Aktualität vor ReadDynamoDB
Fazit