Warum Allzweck-Datenbanken das falsche Fundament für Financial OLTP sind
Amdahls Gesetz und die Grenzen allgemeiner Datenbanken für Hochdurchsatz-Settlement-Workloads. Das Argument für zweckgebundene Engines.
Warum General-Purpose-Datenbanken bei finanziellem OLTP versagen
PostgreSQL kann Finanztransaktionen speichern. Es tut dies zuverlässig für Millionen von Unternehmen. Die Frage ist nicht, ob es sie speichern kann, sondern ob es finanzielle Invarianten mit der Geschwindigkeit und Korrektheit durchsetzen kann, die ein Core-Banking-Ledger erfordert.
Die Antwort, für eine spezifische und wohldefinierte Klasse von Workload, ist nein. Nicht weil PostgreSQL fehlerhaft ist. Weil es designed wurde, ein anderes Problem zu lösen.
Das PostgreSQL-Ledger
Die Standardarchitektur: eine transfers-Tabelle, eine accounts-Tabelle mit einer balance-Spalte (oder eine materialisierte View, die Buchungen summiert), und Anwendungslogik zur Durchsetzung der doppelten Buchführung. Transfers sind INSERT-only. Saldo-Updates werden durch Anwendungscode oder Datenbank-Trigger ausgelöst.
Bei 100 TPS mit 10.000 gleichverteilten Konten funktioniert das ohne Zwischenfall. Abfragen werden in einstelligen Millisekunden abgeschlossen. Lock-Contention ist vernachlässigbar. Das System besteht jeden Test.
Die Obergrenze erscheint bei heißen Konten. Ein Gebühreneinzugskonto, das von 30% aller Transaktionen berührt wird. Ein Settlement-Konto, das ausgehende Zahlungen aggregiert. Ein Plattform-Umsatzkonto, das jede Provisionsaufteilung empfängt. Diese Konten existieren in jedem Finanzsystem. Sie sind strukturell, nicht außergewöhnlich.
Wenn 50 gleichzeitige Transaktionen versuchen, denselben Kontosaldo zu aktualisieren, bilden sie eine Warteschlange. PostgreSQL erwirbt eine Zeilensperre (FOR UPDATE) auf der Saldozeile. Eine Transaktion geht durch; 49 warten. Unter SERIALIZABLE Isolation, die einzige Stufe, die alle Anomalien bei Finanz-Workloads verhindert, kommt zusätzlicher Overhead durch die Konflikterkennungsschicht hinzu. Serialisierungsfehler lösen Retries aus. Retries kaskadieren unter Last.
Die Degradation ist nicht-linear. Bei 200 TPS ist das System in Ordnung. Bei 500 TPS verdoppelt sich die p99-Latenz. Bei 1.000 TPS mit heißen Konten übersteigt die Retry-Rate 20% und der effektive Durchsatz stagniert. Mehr Verbindungen machen es schlimmer, mehr Contention, mehr Retries, mehr verschwendete Arbeit.
Amdahls Gesetz angewandt auf Ledger-Workloads
Gene Amdahl formalisierte diese Obergrenze 1967. Wenn ein Anteil S eines Workloads inhärent seriell ist (nicht parallelisiert werden kann), ist die maximale Beschleunigung durch N parallele Prozessoren:
Speedup = 1 / (S + (1 - S) / N)
Wenn N gegen Unendlich geht, nähert sich der Speedup 1/S. Wenn 5% Ihrer Überweisungen ein heißes Konto berühren, das den Zugriff serialisiert, ist die maximale Beschleunigung 20x, unabhängig von der Hardware. Bei 10% liegt die Obergrenze bei 10x.
Für Finanz-Workloads mit realistischen Kontoverteilungen (Zipf, eine kleine Anzahl von Konten empfängt einen überproportionalen Anteil des Traffics) liegt der serielle Anteil typischerweise bei 5-15%. Die Obergrenze ist real und niedrig.
Die Workarounds sind jedem bekannt, der ein PostgreSQL-basiertes Ledger im großen Maßstab betrieben hat:
Sharding. Konten auf mehrere Datenbanken verteilen. Cross-Shard-Überweisungen (Sender auf Shard A, Empfänger auf Shard B) erfordern Two-Phase Commit, langsam, komplex und ein neuer Fehlermodus. Der operative Overhead der Verwaltung von Shard-Grenzen, Rebalancing und Cross-Shard-Abfragen übersteigt oft den Performance-Gewinn.
Eventual Consistency. Die Isolationsstufe lockern. Akzeptieren, dass Salden vorübergehend falsch sein können. Später abgleichen. Für ein Zahlungssystem, das Kunden Salden anzeigt, bedeutet „vorübergehend falsch" „der Kunde sieht eine Zahl, die nicht sein tatsächlicher Saldo ist." Das verstößt gegen PSD2 Art. 87 (Wertstellungs- und Verfügbarkeitsanforderungen) und zerstört das Vertrauen der Nutzer.
Optimistic Locking mit Retries. Die Contention akzeptieren. Fehlgeschlagene Transaktionen wiederholen. Ein Retry-Budget setzen. Hoffen, dass sich die Contention auflöst, bevor das Budget erschöpft ist. Bei hoher Last degeneriert das zu einem Retry-Storm, bei dem die meiste Datenbankarbeit verschwendet wird.
Application-Level Locking. Redis Advisory Locks verwenden, um den Zugriff außerhalb der Datenbank zu serialisieren. Nun hat man zwei Systeme, die Konsistenz aufrechterhalten, den Lock-Manager und die Datenbank, mit einem Fenster zwischen Lock-Erwerb und Datenbank-Commit, in dem sie sich widersprechen können. Ein Crash in diesem Fenster produziert inkonsistenten Zustand.
Jeder Ansatz tauscht ein Problem gegen ein anderes. Keiner adressiert den fundamentalen Mismatch: PostgreSQLs Zeilensperren-Modell ist für beliebigen gleichzeitigen Zugriff auf beliebige Schemata designed. Finanzielles Settlement ist nicht beliebig. Es ist ein eingeschränkter, hochfrequenter, contention-lastiger Workload, der von einem fundamental anderen Ausführungsmodell profitiert.
Die zweckgebaute Alternative
Eine Settlement-Engine, die für finanzielles OLTP designed wurde, trifft drei Entscheidungen, die eine General-Purpose-Datenbank nicht treffen kann, ohne Allgemeingültigkeit aufzugeben:
Fixed-Size Records. Jedes Konto: 128 Bytes, Cache-Line-aligned. Jede Überweisung: 128 Bytes, Cache-Line-aligned. Keine variablen Felder. Kein TOAST. Keine Overflow-Pages. Die Storage-Engine berechnet die Disk-Position jedes Records per Arithmetik, kein B-Tree-Traversal, kein Index-Lookup. Sequentieller Zugriff wird maximiert. Random I/O wird eliminiert.
Batch-Verarbeitung statt Zeilensperren. Die Engine sammelt Überweisungen in Batches (bis zu 8.190 pro Batch) und verarbeitet sie in einem einzigen Durchgang. Es gibt keine Zeilensperren, weil es keinen gleichzeitigen Zugriff auf einzelne Zeilen gibt. Der Batch ist die Atomizitätseinheit.
Das Durchsatzmodell invertiert sich: Mehr gleichzeitige Clients bedeuten vollere Batches. Vollere Batches bedeuten bessere Amortisation des Per-Batch-Overheads (Konsensrunde, Disk-Flush). Unter steigender Last verbessert sich der Durchsatz, das Gegenteil eines lock-basierten Systems.
Engine-erzwungene Invarianten. Double-Entry ist keine Bibliothek und kein Trigger. Es ist eine Protokolleinschränkung. Eine Überweisung, bei der sum(debits) ≠ sum(credits), wird von der Engine abgelehnt, bevor sie den Speicher erreicht. Eine Überweisung, die ein Konto überziehen würde (wenn das debits_must_not_exceed_credits-Flag gesetzt ist), wird abgelehnt. Deduplizierung ist auf Protokollebene: eine 128-Bit Transfer-ID verhindert Replays ohne anwendungsseitige Idempotenz-Keys.
Kein Anwendungs-Bug kann diese Invarianten umgehen. Sie werden von einem separaten Prozess mit eigenem Speicherraum, eigener Validierung und eigenem Storage erzwungen. Der Blast Radius eines Anwendungsschicht-Fehlers wird durch die Service-Grenze begrenzt.
Das richtige Tool für jede Schicht
Dies ist kein Argument gegen PostgreSQL. PostgreSQL ist die richtige Wahl für:
| Datentyp | Warum PostgreSQL |
|---|---|
| Kundenstammdaten | Relational, abfragbar, variable Felder, Volltextsuche |
| KYC/KYB-Daten | Komplexe verschachtelte Strukturen, JSON-Support, Fremdschlüssel zu Kunden |
| Konfiguration | Key-Value mit Constraints, Audit-Trigger, RLS pro Mandant |
| Jurisdiktionsprofile | Relational mit Feiertagskalendern, Geschäftstage-Regeln |
| Audit-Logs | Append-Only mit Hash-Ketten, abfragbar nach Correlation ID |
| Transaktionsmetadaten | ISO Reason Codes, externe Referenzen, Buchungs-Audit-Trail |
PostgreSQL behandelt all das gut. SQL ist die richtige Abfragesprache für Ad-hoc-Analyse, Reporting und operative Tools.
Das Argument richtet sich gegen die Verwendung von PostgreSQL für eine bestimmte Sache: hochdurchsatzfähiges, contention-freies, strikt serialisierbares finanzielles Settlement. Für diesen Workload, und nur diesen Workload, liefert eine zweckgebaute Engine Eigenschaften, die PostgreSQL nicht ohne Kompromisse erreichen kann, die die Korrektheit untergraben.
Die Architektur: zwei Storage-Schichten, jede für ihren Workload optimiert.
Domain-Daten, Metadaten, Config: Kundenstammdaten, KYC/KYB-Daten, Audit-Logs, Transaktionsmetadaten, Jurisdiktionsprofile. Volle SQL-Mächtigkeit für Abfragen und Reporting.
Nur Ledger-Operationen: Kontosalden, Überweisungsausführung, Double-Entry-Durchsetzung, Two-Phase Commits. Sub-Millisekunden-Latenz, Zero Contention.
PostgreSQL tut, was es am besten kann: strukturierte Daten mit voller SQL-Mächtigkeit speichern und abfragen. Die Settlement-Engine tut eine Sache: Geld zwischen Konten mit strikter Serialisierbarkeit, Zero Contention und engine-erzwungenen Invarianten bewegen. Keines der Systeme versucht, das andere zu sein.
Bewertungskriterien
Drei Fragen für jede Ledger-Storage-Bewertung. Die Antworten sind messbar.
1. Was passiert, wenn 100 gleichzeitige Überweisungen dasselbe Konto treffen?
Messen Sie p99-Latenz, nicht p50. P50 sagt Ihnen den typischen Fall. P99 sagt Ihnen, was unter Contention passiert, das einzige Szenario, das für heiße Konten zählt. Wenn p99 unter Hot-Account-Last um mehr als 10x degradiert, hat das System eine Contention-Obergrenze.
2. Kann ein Bug in Ihrem Anwendungscode Geld erzeugen?
Wenn die Double-Entry-Invariante im Anwendungscode erzwungen wird (eine Bibliothek, ein Trigger, eine Middleware), lautet die Antwort ja. Ein Bug in der Durchsetzungsschicht, eine fehlende Prüfung, eine Race Condition, eine Exception, die die Validierung überspringt, kann eine Überweisung produzieren, bei der Belastungen ≠ Gutschriften. Wenn die Invariante von einer separaten Engine auf Protokollebene erzwungen wird, lautet die Antwort nein.
3. Was ist Ihre Deduplizierungsstrategie?
Wenn es ein anwendungsseitiger Idempotenz-Key ist (eine UUID in einer Tabelle, geprüft vor jeder Überweisung), was passiert, wenn die Key-Generierungslogik einen Bug hat? Was passiert, wenn Prüfung und Überweisung nicht in derselben atomaren Operation sind? Deduplizierung auf Protokollebene, bei der die Engine selbst doppelte Transfer-IDs ablehnt, eliminiert diese Problemklasse.
Weiterlesen: Das Ledger | Deterministisches Settlement
Quellen:
- Amdahl, Gene M. „Validity of the single processor approach to achieving large scale computing capabilities." AFIPS '67, 1967.
- PostgreSQL Dokumentation: Row-Level Locking, Serializable Isolation (https://www.postgresql.org/docs/current/transaction-iso.html)
- PSD2, Richtlinie 2015/2366, Art. 87 (Wertstellungsdatum und Verfügbarkeit von Geldern)
- „Scalability! But at what COST?" (McSherry et al., USENIX HotOS'15), warum Scale-Out nicht immer die Antwort für write-heavy OLTP ist