OLTP-Datenbankarchitektur: Von Prinzipien zur Implementierung in Rust
Wichtige OLTP-Engine-Subsysteme wurden implementiert: einheitlicher Speicher in einer einzelnen Datei pro Datenbank, speicherbasierte Speicherung mit UNDO-Log-MVCC, WAL- und ARIES-basierte Wiederherstellung, ein gemeinsamer BufferPool, eine In-Memory-Engine und ein mit PostgreSQL kompatibles pgwire-Protokoll. Der Code ist funktionsfähig, erfordert jedoch Lasttests und Stabilisierung von Randfällen. Es handelt sich nicht um ein Endprodukt, sondern um eine frühe Phase mit etablierten Verträgen.
Kernentwurfsprinzipien
Das Projekt stützt sich auf strenge Regeln für Entscheidungsfindung. Jede neue Entscheidung muss sich daran halten oder Abweichungen begründen.
Restriktiv als Standard
Komponenten haben explizite Grenzen mit Fail-Closed-Verhalten anstelle von Drosselung. Dies gewährleistet Vorhersagbarkeit unter Last.
| Komponente | Grenze | Aktion bei Verletzung | SQLSTATE |
|-----------|---------|------------------------|----------|
| BufferPool | buffer_pool_size_mb | Auslagerung (CLOCK), WAL-first Flush | — |
| TxnWriteSet | txn_max_write_set_mb | DML ablehnen | 54023 |
| UndoStore | undo_max_size_mb | Schreibvorgänge ablehnen | 53100 |
| Verbindungspool | max_connections | Neue Verbindungen ablehnen | 53300 |
| Statement-Timeout | statement_timeout_ms | Abfrage abbrechen | 57014 |
| Snapshot-Alter | max_snapshot_age | Veraltete Snapshots zwangsweise schließen | 40001 |
Fail-Closed bietet klare SQLSTATE-Codes für Anwendungs-Wiederholungs- oder Circuit-Breaker-Logik.
Vertragsorientierter Ansatz
Architektonische Verträge sind in Rust-Traits definiert: TableEngine, PageProvider, TransactionLogSink, StorageIo. Dies verhindert Implementierungsabweichungen durch Durchsetzung von Invarianten zur Kompilierzeit.
Warum Rust?
Rust wurde für seine vorhersagbare Latenz, Speicherzuweisungskontrolle und Typsicherheit gewählt. Send/Sync an Async/Sync-Grenzen, explizite Fehler statt Laufzeitprobleme. C++ erfordert zusätzliche Disziplin, während Go aufgrund von GC für OLTP-Tail-Latenz ungeeignet ist.
Trennung der Verantwortlichkeiten
Die Datenbank fungiert als Daten-Engine ohne Geschäftslogik: zunächst keine Trigger, PL/pgSQL oder gespeicherte Prozeduren. Benutzerdefinierte Funktionen können später mit Sandboxing und ohne Nebeneffekte hinzugefügt werden.
Hybride Asynchronität
- Netzwerk-/pgwire-Schicht: asynchron (Annahme, TLS, Senden).
- Kern (Abfrageausführung, Speicher, WAL, MVCC): synchron für Korrektheit und Debugging.
- Grenze ist unidirektional über eine Brücke.
StorageIo ist abstrahiert, um zukünftige asynchrone I/O ohne Neuschreiben der Transaktionslogik zu ermöglichen.
| Schicht | Laufzeit | Beispiele |
|------|---------|---------|
| Netzwerk/Protokoll | asynchron | pgwire, TLS |
| Abfrageausführung | synchron | Plan, Ausführung |
| Speicher/WAL | synchron (asynchrone I/O später) | HeapStore, BufferPool |
| MVCC/Transaktionen | synchron | Snapshot, Sperren |
PostgreSQL-Kompatibilität
Pgwire ermöglicht Integration mit Treibern und ORMs ohne Client-Migrationen. Kompatibilität erfolgt auf Protokollebene über eine Grenzschicht, ohne PostgreSQL-Interna (MVCC-Heap, VACUUM) zu kopieren. Der Kern behält architektonische Freiheit.
Plattformbeschränkungen und Zuverlässigkeit
Nur Linux, um sich auf io_uring und eBPF ohne plattformübergreifende Kompromisse zu konzentrieren. Prioritäten: keine Datenkorruption, sichere Rücknahme von Optimierungen, Resilienz gegenüber Benutzereingaben, integrierte Sicherheit.
Wichtige Erkenntnisse
- Restriktiv als Standard mit Fail-Closed für Vorhersagbarkeit unter Last.
- Vertragsorientiert über Rust-Traits für Invariantenkontrolle.
- Hybride Async/Sync: Kernstabilität vor I/O-Beschleunigung.
- Pgwire-Kompatibilität ohne Kopieren von PostgreSQL-Interna.
- Daten-Engine ohne Geschäftslogik für Reinheit und Testbarkeit.
— Editorial Team
Noch keine Kommentare.