Relationale Datenbanken begleiten uns durch unsere gesamte Karriere. Aber was, wenn man Millionen von Ereignissen pro Sekunde schreiben, Daten über Rechenzentren hinweg replizieren und dabei eine Verfügbarkeit von 99,99 % garantieren muss? Weder PostgreSQL noch Oracle können das leisten. Wir begannen mit Apache Cassandra zu experimentieren.
Warum keine relationale Datenbank?¶
Für einen Telekommunikationskunden bauten wir ein System zur Erfassung von CDR-Datensätzen (Call Detail Records). Anforderungen: 50.000 Schreibvorgänge pro Sekunde, 2 Jahre Aufbewahrung, Datenzugriff von zwei geografisch getrennten Standorten. Der klassische Ansatz — PostgreSQL mit Partitioning — stieß an die Grenzen der vertikalen Skalierung. RAM und CPU auf einem einzelnen Server aufzustocken hat seine Grenzen.
Cassandra bietet horizontale Skalierung — fügen Sie einen neuen Knoten zum Cluster hinzu und die Daten werden automatisch umverteilt. Kein Single Point of Failure, kein Master-Knoten. Jeder Knoten ist gleichberechtigt.
Architektur: Ring und Partitioning¶
Cassandra organisiert Knoten in einem logischen Ring. Jede Datenzeile hat einen Partition Key, aus dem ein Hash berechnet wird. Der Hash bestimmt, auf welchem Knoten die Daten liegen. Mit einem Replikationsfaktor von 3 wird jede Zeile auf drei Knoten gespeichert — fällt einer aus, sind die Daten weiterhin verfügbar.
CREATE KEYSPACE telco_cdr
WITH replication = {
'class': 'NetworkTopologyStrategy',
'dc_prague': 3,
'dc_brno': 3
};
CREATE TABLE telco_cdr.call_records (
phone_number text,
call_date date,
call_time timestamp,
duration int,
called_number text,
cell_id text,
PRIMARY KEY ((phone_number, call_date), call_time)
) WITH CLUSTERING ORDER BY (call_time DESC);
Das Design des Partition Key ist entscheidend. Wir wählten den zusammengesetzten Schlüssel (phone_number, call_date) — jede Partition enthält die Datensätze einer Nummer für einen Tag. Das gewährleistet eine gleichmäßige Datenverteilung und effiziente Abfragen vom Typ „Zeige mir die Anrufe der Nummer X am Datum Y”.
Tunable Consistency¶
Im Gegensatz zu relationalen Datenbanken mit ACID-Transaktionen bietet Cassandra einstellbare Konsistenz. Für jede Abfrage wählt man eine Stufe:
- ONE — Antwort von einem Knoten, am schnellsten, aber Risiko veralteter Daten
- QUORUM — Antwort von der Mehrheit der Replikas, ein guter Kompromiss
- ALL — Antwort von allen Replikas, am langsamsten, aber starke Konsistenz
- LOCAL_QUORUM — Quorum innerhalb eines einzelnen Rechenzentrums, ideal für Geo-Replikation
Für das Schreiben von CDR-Datensätzen wählten wir LOCAL_QUORUM — Daten werden lokal bestätigt und asynchron ins zweite DC repliziert. Für das Lesen analytischer Berichte verwenden wir ebenfalls LOCAL_QUORUM, was Read-after-Write-Konsistenz innerhalb eines Standorts garantiert.
Datenmodellierung: Query-First-Ansatz¶
Der größte mentale Umbruch gegenüber relationalen Datenbanken: In Cassandra modelliert man Daten um Abfragen herum, nicht um Entitäten. Normalisierung gibt es nicht. Datenduplizierung ist normal und erwünscht.
Wenn man dieselben Daten auf zwei verschiedene Arten anzeigen muss (nach Nummer und nach Netzwerkzelle), erstellt man zwei Tabellen mit denselben Daten, aber unterschiedlichen Partition Keys. Man schreibt gleichzeitig in beide. Speicherplatz ist günstig, Abfragelatenz ist teuer.
Betrieb in der Produktion¶
Compaction¶
Cassandra schreibt Daten in unveränderliche Dateien (SSTables). Der Compaction-Prozess führt sie periodisch zusammen. Wir wählten DateTieredCompactionStrategy — optimal für Zeitreihendaten, bei denen sich ältere Datensätze nicht ändern. Compaction ist CPU- und I/O-intensiv; wir planen sie außerhalb der Spitzenzeiten.
Repair¶
Wenn ein Knoten offline war und Schreibvorgänge verpasst hat, können die Daten darauf veraltet sein. Der Befehl nodetool repair synchronisiert Daten zwischen Replikas. Wir führen ihn einmal pro Woche auf jedem Knoten aus — es ist notwendige Wartung, belastet aber den Cluster.
Monitoring¶
Cassandra exportiert Metriken über JMX. Wir überwachen vor allem: Read/Write-Latenz (p99 unter 10 ms), Compaction Pending (sollte nicht wachsen), Heap-Nutzung und Tombstone-Anzahl. Zu viele Tombstones (Löschungen in Cassandra) verlangsamen das Lesen — daher bevorzugen wir TTL gegenüber explizitem DELETE.
Cassandra vs. MongoDB¶
Beide sind NoSQL, aber sie lösen unterschiedliche Probleme. MongoDB glänzt bei flexiblen Schemas und Ad-hoc-Abfragen — es ähnelt eher einer relationalen Datenbank. Cassandra überzeugt bei Schreibleistung, horizontaler Skalierung und Multi-DC-Replikation. Für unsere CDR-Datensätze (schreibintensiv, Zeitreihen, geo-verteilt) war Cassandra die klare Wahl.
Ergebnisse¶
Ein Cluster mit sechs Knoten (3 in jedem DC) verarbeitet 80.000 Schreibvorgänge/s mit einer p99-Latenz unter 5 ms. Das Lesen analytischer Abfragen (Anrufe einer Nummer über einen Monat) dauert 15–30 ms. Daten replizieren sich zwischen Prag und Brünn mit einer Latenz unter 50 ms. In sechs Monaten Betrieb hatten wir null Ausfallzeit — selbst bei geplanter Wartung (Rolling Restart) lief der Cluster ohne Unterbrechung.
Cassandra ist kein Ersatz für eine relationale Datenbank¶
Cassandra ist ein spezialisiertes Werkzeug für bestimmte Anwendungsfälle: schreibintensive Workloads, Zeitreihendaten, geo-verteilte Systeme. Wenn Sie JOINs, Ad-hoc-Abfragen oder ACID-Transaktionen brauchen, bleiben Sie bei PostgreSQL.
Aber wenn Ihre Anforderungen passen — und wir glauben, dass es mit wachsenden Datenvolumen immer mehr solcher Projekte geben wird — ist Cassandra eine außergewöhnlich zuverlässige Lösung.
Brauchen Sie Hilfe bei der Implementierung?
Unsere Experten helfen Ihnen bei Design, Implementierung und Betrieb. Von der Architektur bis zur Produktion.
Kontaktieren Sie uns