NoSQL und das CAP-Theorem
Warum es neben Tabellen auch Dokumente, Schlüssel-Wert-Speicher und Graphen gibt – und welche Entscheidung jede verteilte Datenbank treffen muss, wenn das Netz zwischen ihren Rechnern reißt.
NoSQL ist ein Sammelbegriff für Datenbanken, die keine klassischen Tabellen benutzen: Schlüssel-Wert-Speicher, Dokument-Datenbanken, Spalten-Datenbanken und Graph-Datenbanken. Jede ist für ein bestimmtes Zugriffsmuster gebaut und lässt sich oft leichter auf viele Rechner verteilen als eine relationale Datenbank.
Sobald Daten auf mehreren Rechnern liegen, gilt das CAP-Theorem: Reißt das Netz zwischen ihnen, muss das System wählen – entweder alle sehen garantiert denselben Stand (Konsistenz), oder jeder Rechner antwortet weiter, auch wenn sein Stand veraltet sein könnte (Verfügbarkeit). Beides zugleich geht nicht.
Warum es NoSQL gibt
Relationale Datenbanken wachsen klassisch vertikal: größerer Server, mehr Speicher, schnellere Platten. Irgendwann gibt es keinen größeren Server mehr. Mitte der 2000er-Jahre standen Google und Amazon genau dort und bauten eigene Systeme, die horizontal wachsen – einfach mehr gewöhnliche Rechner dazu. Die Beschreibungen von Googles Bigtable (2006) und Amazons Dynamo (2007) wurden zu Bauplänen einer ganzen Generation. 2009 bekam die Bewegung ihren Namen, heute meist gelesen als „Not only SQL“.
Der Preis: Was relationale Datenbanken mühelos können – JOINs über beliebige Tabellen, Transaktionen über beliebige Zeilen –, wird auf vielen Rechnern teuer. NoSQL-Systeme verzichten deshalb oft darauf und verlangen, dass man die Daten so ablegt, wie man sie später abfragt.
Die vier Familien
Dieselben Daten – Kundin Anna mit zwei Bestellungen – in fünf Modellen:
Daten verteilen: Replikation und Sharding
Zwei Techniken stecken hinter jeder verteilten Datenbank, oft kombiniert:
Replikation
Dieselben Daten liegen auf mehreren Rechnern. Fällt einer aus, übernehmen die anderen, und Lesezugriffe verteilen sich. Die Schwierigkeit: alle Kopien aktuell halten.
Sharding
Die Daten werden aufgeteilt: Kunden A–M auf Rechner 1, N–Z auf Rechner 2. Jeder Rechner trägt nur einen Teil der Last. Die Schwierigkeit: Anfragen über mehrere Teile.
Hash-Verteilung
Statt nach Alphabet verteilt man meist nach einem Hash des Schlüssels – so landen die Daten gleichmäßig verteilt, ohne dass ein Rechner alle „M“-Namen abbekommt.
Die naheliegende Formel lautet: Rechner = Hash(Schlüssel) mod Anzahl. Das funktioniert – bis ein Rechner dazukommt. Dann ändert sich das Ergebnis für fast jeden Schlüssel, und fast alle Daten müssten umziehen. Consistent Hashing löst das: Rechner und Schlüssel werden auf einen gedachten Ring abgebildet, jeder Schlüssel gehört dem nächsten Rechner im Uhrzeigersinn. Ein neuer Rechner übernimmt nur ein Stück des Rings.
Das CAP-Theorem
Eric Brewer stellte im Jahr 2000 eine Vermutung auf, die Seth Gilbert und Nancy Lynch 2002 bewiesen. Ein verteiltes System kann von diesen drei Eigenschaften nicht alle gleichzeitig garantieren:
Consistency
Jedes Lesen liefert den neuesten geschriebenen Wert – oder einen Fehler. Alle sehen dasselbe.
Availability
Jede Anfrage an einen funktionierenden Rechner bekommt eine Antwort – keinen Fehler.
Partition tolerance
Das System arbeitet weiter, auch wenn das Netz zwischen seinen Rechnern unterbrochen ist.
Wichtig ist, wie man das liest. Netzunterbrechungen passieren – man kann P nicht abwählen. Die eigentliche Aussage ist: Wenn das Netz reißt, muss man zwischen C und A wählen. Spiel es durch:
Keine der beiden Antworten ist falsch, es kommt auf die Daten an. Ein Kontostand oder der letzte Sitzplatz verlangt CP – lieber eine Fehlermeldung als eine Doppelbuchung. Ein Warenkorb, ein Like-Zähler oder ein Social-Media-Feed verträgt AP – lieber kurz veraltet als „Seite nicht verfügbar“. Amazon hat Dynamo genau deshalb als AP-System gebaut: Ein Warenkorb, der Artikel doppelt enthält, ist ärgerlich; einer, der sich nicht öffnen lässt, kostet Umsatz.
Eine Erweiterung, PACELC, ergänzt den Normalfall: Auch ohne Netzunterbrechung muss man abwägen – zwischen niedriger Latenz und strenger Konsistenz. Denn auf die Bestätigung aller Kopien zu warten, kostet bei jedem Schreiben Zeit.
Quoren: Konsistenz zum Einstellen
Viele Systeme lassen die Wahl pro Anfrage offen. Bei N Kopien bestätigt ein Schreiben, sobald W Kopien es haben, und ein Lesen fragt R Kopien. Ist R + W größer als N, überschneiden sich Lese- und Schreibmenge immer – mindestens eine gefragte Kopie kennt den neuesten Wert.
Konsistenzmodelle
Zwischen „alle sehen sofort dasselbe“ und „irgendwann stimmt es“ gibt es Abstufungen, die in der Praxis viel ausmachen:
| Modell | Garantie | Spürbar als |
|---|---|---|
| Stark (linearisierbar) | Jedes Lesen sieht das neueste Schreiben | verhält sich wie eine einzige Datenbank |
| Read your writes | Ich sehe immer meine eigenen Änderungen | kein „Ich habe doch gerade gespeichert – wo ist es?“ |
| Monotones Lesen | Ich sehe nie einen älteren Stand als vorhin | Kommentare verschwinden nicht beim Neuladen |
| Eventual Consistency | Ohne neue Schreibvorgänge stimmen irgendwann alle Kopien überein | kurz veraltete Zähler, Listen, Feeds |
Wann was?
| Bedarf | Typische Wahl |
|---|---|
| Geschäftsdaten mit Beziehungen und Transaktionen | relational – PostgreSQL, MySQL, Oracle |
| Cache, Sitzungen, Zähler, Ranglisten | Schlüssel-Wert im Speicher – Redis, Valkey |
| Objekte mit wechselnder Struktur, als Ganzes gelesen | Dokument – MongoDB, oder jsonb in PostgreSQL |
| Riesige Schreiblast, Messwerte, Ereignisse, weltweit verteilt | Spalten – Cassandra, ScyllaDB; Zeitreihen – InfluxDB, TimescaleDB |
| Beziehungen über viele Ecken: Netzwerke, Empfehlungen, Betrugserkennung | Graph – Neo4j |
| Volltextsuche | Suchmaschine – Elasticsearch, OpenSearch |
| SQL und Transaktionen, aber weltweit verteilt | „NewSQL“ – Google Spanner, CockroachDB |
Die Grenzen verschwimmen: PostgreSQL speichert und indiziert JSON, MongoDB kann Transaktionen über mehrere Dokumente, und verteilte SQL-Datenbanken zeigen, dass starke Konsistenz und horizontales Wachstum sich nicht ausschließen – sie kosten nur Latenz. Viele Systeme setzen mehrere Datenbanken nebeneinander ein, jede für ihren Zweck.
Für Entwickler übersetzt
// Spring Data MongoDB: ein Dokument mit eingebetteten Bestellungen
@Document("kunden")
record Kunde(@Id String id, String name, List<Bestellung> bestellungen) {}
record Bestellung(long nr, LocalDate datum, BigDecimal summe) {}
interface KundenRepository extends MongoRepository<Kunde, String> {
List<Kunde> findByName(String name); // Abfrage aus dem Methodennamen
}
// Spring Data Redis: Schlüssel-Wert mit Ablaufzeit – ein klassischer Cache
redis.opsForValue().set("sitzung:a81f", kundeId, Duration.ofMinutes(30));| Thema | Was man wissen sollte |
|---|---|
| Spring Data | Dieselbe Repository-Idee für JPA, MongoDB, Redis, Cassandra, Neo4j und Elasticsearch – die Abfragen unterscheiden sich trotzdem, weil die Modelle sich unterscheiden. |
| Datenmodell nach Abfrage | In Dokument- und Spalten-Datenbanken entwirft man von den Abfragen her: Was zusammen gelesen wird, gehört zusammen gespeichert. Das Gegenteil der Normalisierung. |
| Eventual Consistency im Code | Nach dem Speichern sofort neu lesen kann den alten Stand liefern. Entweder mit der eigenen Antwort weiterarbeiten oder für dieses Lesen stärkere Konsistenz anfordern. |
| Schema im Code | „Schemalos“ heißt: Die Datenbank prüft nichts. Das Schema steckt dann in den Java-Klassen – alte Dokumente mit alten Feldern muss der Code weiter verstehen. |