Wie du eine Datenbank wählst
Kurz gesagt: Wähle eine Datenbank nach deinen Daten und den Fragen, die du an sie stellst: Eine relationale Datenbank hält die Wahrheit, und eine andere Art kommt nur dort daneben, wo sie schwach ist.
Problem
Viele Arten von Datenbanken auf dem Tisch
Das Modul hat Key-Value, Dokumente, Ereignisströme, Graphen, Spalten und Zeitreihen, Textsuche, Vektoren und Cluster gezeigt. Jede dieser Datenbanken ist in etwas hervorragend. Das neue Projekt des Shops fragt: Welche wählen wir?
Der falsche Weg ist, bei der Datenbank anzufangen („wir nehmen MongoDB, weil es modern ist“). Der richtige Weg ist, bei den Daten und den Fragen anzufangen, die du an sie stellen wirst, und eine neue Datenbank erst dazuzunehmen, wenn es eine Frage gibt, die die Hauptdatenbank nicht gut beantwortet.
Die Idee
Fünf Fragen
- Welche Form haben die Daten? Zeilen mit denselben Spalten, ganze Dokumente mit verschachtelten Teilen, Verbindungen zwischen Dingen, Einträge über die Zeit, freier Text.
- Was fragst du? Eine Sache über einen Schlüssel, einen Bericht über alles, „wer ist mit wem verbunden“, „welche Wörter“, „was ist ähnlich“.
- Wie wichtig ist Konsistenz? Geld und Bestand brauchen Transaktionen und das Lesen des letzten Schreibvorgangs. Ein Like-Zähler darf eine Sekunde hinterherhinken.
- Wie groß sind die Daten und die Last? Tausende oder Milliarden Zeilen, zehn- oder hunderttausend Abfragen pro Sekunde. Die meisten Projekte sind viel kleiner, als sie denken.
- Wer kümmert sich darum? Jede Datenbank braucht Kopien, Monitoring, Updates und jemanden, der weiß, was zu tun ist, wenn sie ausfällt.
| Die Frage | Art der Datenbank |
|---|---|
| Geld, Bestellungen, Bestand, alles, was stimmen muss | relational (PostgreSQL, MySQL) |
| Dasselbe tausendfach über einen Schlüssel, Sitzungen, Warenkörbe | Key-Value (Redis) |
| Ganze Objekte mit verschiedenen Strukturen | Dokumentendatenbank (MongoDB) oder JSONB |
| Viele Systeme erfahren vom selben Ereignis | Ereignisstrom (Kafka) |
| Empfehlungen, Netzwerke, Betrug über mehrere Schritte | Graph (Neo4j) |
| Berichte über Millionen Zeilen, Sensoren, Metriken | spaltenorientiert, Zeitreihen (ClickHouse, TimescaleDB) |
| Ein Suchfeld, Wörter im Text | Volltextsuche (Elasticsearch) |
| „Ähnlich in der Bedeutung“, ein Chatbot über Dokumente | Vektor (pgvector, Qdrant) |
Die Idee
Fang mit PostgreSQL an
Für die meisten neuen Projekte ist ein einziger relationaler Datenbankserver der vernünftige Start. PostgreSQL allein deckt einen großen Teil des Moduls ab: JSONB-Spalten für Dokumente, tsvector für die Textsuche, pgvector für Vektoren, TimescaleDB für Zeitreihen, Lese-Replikate und das Aufteilen von Tabellen nach Zeit. Es ist nicht in jedem davon das Beste, aber gut genug, und eine Datenbank ist viel einfacher zu betreuen als fünf.
Eine eigene Datenbank lohnt sich, wenn es eine konkrete Frage gibt, die die Hauptdatenbank langsam oder schwer beantwortet, und du das gemessen hast: der Cache, weil dieselbe Abfrage 2000-mal pro Minute läuft; die Suchmaschine, weil die Suche die Hauptaufgabe der Seite ist; das Data Warehouse, weil die Berichte die Kasse bremsen.
Genauer, für Neugierige
Das nennt man Polyglot Persistence: verschiedene Datenbanken für verschiedene Teile des Systems. Es funktioniert gut, wenn eine davon klar die Quelle der Wahrheit ist und die anderen Kopien sind, die sich neu aufbauen lassen. Es funktioniert schlecht, wenn zwei Datenbanken verschiedene Teile derselben Wahrheit halten und niemand weiß, welche stimmt.
Sieh es dir an
Der Shop, wenn er wächst
So könnte der Shop nach ein paar Jahren aussehen, jeder Teil dann dazugekommen, als er gebraucht wurde:
- PostgreSQL hält die Kunden, die Produkte, die Bestellungen und die Zahlungen. Das ist die Wahrheit. Sie hat ein synchrones und ein asynchrones Replikat.
- Redis hält den Cache des Katalogs, die Sitzungen und die Warenkörbe.
- Kafka trägt die Ereignisse „Bestellung aufgegeben“, „bezahlt“, „versandt“ zum Lager, zu den Rechnungen und zur E-Mail.
- Elasticsearch hält eine Kopie der Produkte und Bewertungen für das Suchfeld. Es wird aus den Ereignissen gefüllt.
- ClickHouse ist das Data Warehouse für die Berichte. Es wird jede Nacht oder aus dem Strom gefüllt.
- pgvector in derselben PostgreSQL liefert „ähnliche Produkte“.
Alle Kopien kommen aus PostgreSQL. Geht der Index in Elasticsearch kaputt, wird er aus der Datenbank neu aufgebaut. Umgekehrt geht das nicht.
Problem
Die zwei Schreibvorgänge
Die Bestellung wird nach PostgreSQL geschrieben, und dann sendet das Programm ein Ereignis an Kafka. Stürzt das Programm dazwischen ab, steht die Bestellung in der Datenbank, aber das Ereignis fehlt: Das Lager packt nie das Paket. Ist es umgekehrt (erst das Ereignis, dann die Datenbank), packt das Lager vielleicht ein Paket für eine Bestellung, die später scheitert.
Die zwei Systeme haben keine gemeinsame Transaktion. Die Lösung ist eine Outbox-Tabelle: Das Ereignis wird in eine Tabelle in derselben Datenbank geschrieben, in derselben Transaktion wie die Bestellung. Entweder werden beide geschrieben, oder keins.
published_at ist NULL: Das Ereignis wurde noch nicht gesendet. Ein eigenes Programm (ein Relay) liest die ungesendeten der Reihe nach, sendet sie an den Strom und markiert sie erst dann:
Und schlägt vor dem COMMIT etwas fehl, gibt es weder eine Änderung noch ein Ereignis:
Genauer, für Neugierige
Das Relay kann abstürzen, nachdem es das Ereignis gesendet hat, aber bevor es es markiert. Dann sendet es beim Neustart erneut. Deshalb müssen die Empfänger des Ereignisses idempotent sein, wie die Rechnungen in der Lektion über Ströme. Statt eines Programms, das die Tabelle liest, nimmt man oft Debezium: Es liest das Änderungsprotokoll von PostgreSQL (das WAL) und sendet jede neue Zeile der Outbox an Kafka. Das heißt Change Data Capture (CDC).
Probier es aus
Eine Bestellung stornieren
Der Kunde storniert Bestellung 42. Ändere in einer Transaktion ihren Status auf cancelled und schreibe ein Ereignis in die Outbox für das Topic orders mit dem Schlüssel order-42 und dem Inhalt {"order":42,"event":"cancelled"}.
Lösung
BEGIN;
UPDATE orders SET status = 'cancelled' WHERE id = 42;
INSERT INTO outbox (topic, key, payload)
VALUES ('orders', 'order-42', '{"order":42,"event":"cancelled"}');
COMMIT;BEGIN, UPDATE orders SET status = 'cancelled' WHERE id = 42, INSERT INTO outbox (topic, key, payload) VALUES ('orders', 'order-42', '…') und COMMIT. Nur das UPDATE ändert den Status, aber das Lager und die E-Mail erfahren nie davon. Der Schlüssel ist die Bestellnummer, damit die Stornierung in dieselbe Partition kommt wie „aufgegeben“ und „bezahlt“ und nach ihnen gelesen wird.
Übung
Fünf Projekte
Für jedes Projekt: Welche Datenbank ist das Fundament, oder was fügst du daneben hinzu?
Test
Teste dich
Verwandte Lektionen
Abschnitt „Verwandte Lektionen“- Baut auf: Spaltenorientierte Datenbanken und Zeitreihen · Text durchsuchen · Suchen nach Bedeutung · Replikation, Sharding und CAP
- Aufgaben zum Thema: Arten von Datenbanken