2026-06-13 00:02:27 +02:00
2026-06-12 22:23:14 +02:00
2026-06-13 00:02:27 +02:00

Table of Contents

paulDB 🦀🏳️‍🌈

Eine eigene HTAP-Datenbank in Rust. Row- und Column-Storage in einer In-Memory-Engine, verbunden durch ein Delta-Merge-Konzept nach dem Vorbild von SAP HANA. Gebaut, um zu verstehen nicht nur zu benutzen.

Status: 🌱 Vision-Phase · Sprache: Rust · Modell: In-Memory HTAP · Lizenz: MIT


Was ist paulDB?

paulDB ist mein Versuch, eine Datenbank von Grund auf selbst zu schreiben in Rust. Nicht, weil die Welt noch eine Datenbank braucht, sondern weil ich wissen will, warum Datenbanken so funktionieren, wie sie funktionieren.

Der rote Faden ist eine konkrete, ehrgeizige Frage:

Wie hält man OLTP und OLAP in einem System gleichzeitig schnell also HTAP und wie macht SAP HANA das mit Delta und Main?

paulDB ist die Antwort, die ich mir selbst mit Code gebe. Mein „Crafting Interpreters", nur für Datenbanken.


Der Nordstern: HTAP

Klassische Systeme zwingen zur Wahl zwischen zwei Welten:

Workload Optimiert für Speicherform Beispiel-Query
OLTP viele kleine Schreib-/Lesezugriffe Row-Store (Zeilen) INSERT … · SELECT … WHERE id = 42
OLAP wenige große Aggregationen Column-Store (Spalten) SELECT kat, SUM(wert) … GROUP BY kat

HTAP (Hybrid Transactional/Analytical Processing) will beides in einem System. Genau das ist der einzige Maßstab, an dem paulDB jede Entscheidung misst: Was den HTAP-Beweis schärft, kommt zuerst. Alles andere wartet.


Das HANA-Vorbild: Delta & Main

SAP HANA löst HTAP elegant über zwei Bereiche pro Tabelle:

flowchart LR
    W[Schreibzugriffe] --> DELTA["DELTA<br/>Row-Store · schreib-optimiert"]
    R[Leseanalysen] --> MAIN["MAIN<br/>Column-Store · lese-optimiert, komprimiert"]
    DELTA -- "Delta-Merge (periodisch)" --> MAIN
  • Delta schreib-optimiert, nimmt neue Daten schnell auf (OLTP-Seite).
  • Main lese-optimiert, stark komprimiert, ideal für Analytik (OLAP-Seite).
  • Delta-Merge schiebt periodisch Delta → Main und hält Lesezugriffe schnell.

Genau dieses Prinzip ist das Herzstück von paulDB.


Architektur (in-memory, HTAP-zentriert)

In-Memory ist hier kein Kompromiss es ist authentisch: HANA ist primär in-memory, Delta und Main leben im RAM. Persistenz auf Platte ist deshalb eine optionale spätere Etappe, nicht das Fundament.

flowchart TD
    SQL["SQL-Frontend<br/>Tokenizer → AST → Planner"]
    ROUTER{"Query-Router<br/>OLTP oder OLAP?"}
    DELTA["DELTA · Row-Store<br/>(in-memory, schreib-optimiert)"]
    MAIN["MAIN · Column-Store<br/>(in-memory, komprimiert)"]
    MERGE["Delta-Merge-Worker<br/>(periodisch)"]
    SQL --> ROUTER
    ROUTER -->|"INSERT / Punkt-SELECT"| DELTA
    ROUTER -->|"GROUP BY / Aggregation"| MAIN
    DELTA -. "Lese-Queries sehen Delta  Main" .-> MAIN
    DELTA ==>|merge| MERGE ==> MAIN

Kernideen im Detail

Delta (Row) vs. Main (Column)

Eine Zeile in den Delta-Store zu schreiben ist billig man hängt sie hinten an. Über eine Spalte im Main-Store zu aggregieren ist billig alle Werte einer Spalte liegen zusammenhängend und cache-freundlich im Speicher. paulDB nutzt beide Stärken und überbrückt sie mit dem Merge.

Kompression im Column-Store der Deep-Dive 🔬

Hier gehen wir so realistisch und tief wie möglich und bauen HANAs zweistufiges Modell nach.

Stufe 1 Dictionary-Encoding (immer): Jede Spalte bekommt ein sortiertes Wörterbuch ihrer distinct-Werte. Die eigentliche Spalte wird zu einem Vektor aus Integer-Value-IDs (Index ins Wörterbuch). Das allein macht die Spalte klein und macht Scans/Aggregationen schnell, weil man über kompakte Integer statt über Strings läuft.

Stufe 2 Advanced Compression auf den Value-ID-Vektor. Pro Spalte wird das günstigste Verfahren gewählt:

Verfahren Idee Stark bei
Prefix häufigen Anfangswert einmal speichern Spalten mit dominantem Startwert
Run-Length (RLE) „Wert × Anzahl" statt Wiederholungen langen Wiederholungsläufen
Cluster Blöcke aus wiederkehrenden Mustern lokal geclusterten Werten
Sparse häufigsten Wert weglassen, nur Ausnahmen speichern dünn besetzten Spalten
Indirect gemeinsame Werte über Blöcke indirekt referenzieren mittlerer Kardinalität

Das ist exakt die Familie, die HANA auf seinen Column-Store legt und genau das Rabbit-Hole, in dem das „Warum" wohnt.

Query-Router = der HTAP-Beweis

Der Router ist der Moment, in dem paulDB sichtbar HTAP wird: dieselbe Engine bedient ein SELECT … WHERE id = … über den Row-Pfad (Delta + Main) und ein … GROUP BY … über den Column-Pfad (Main). Ein Lese-Query sieht dabei immer Delta Main, damit frisch geschriebene Daten sofort in der Analytik auftauchen.


Roadmap

Etappenweise jede Stufe ist für sich ein vollständiges Lernziel. (Reihenfolge ist Plan, kein Versprechen an einen Zeitpunkt. Erst wenn das SAP-Fundament steht.)

  • E0 Rust-Fundament + Repo-Setup · cargo, Projektstruktur, Tests, CI
  • E1 In-Memory Row-Store (Delta) · Tabellen, Schema, INSERT, Full-Scan
  • E2 Mini-SQL-Parser · Tokenizer → AST → Executor: INSERT + SELECT … WHERE
  • E3 In-Memory Column-Store (Main) · spaltige Daten + tiefe Kompression (Dictionary → Prefix/RLE/Cluster/Sparse/Indirect)
  • E4 Delta-Merge ❤️ · periodischer Merge Delta(row) → Main(column) — der Nordstern
  • E5 Query-Router · Punktabfrage → Row-Pfad, Aggregation → Column-Pfad — der HTAP-Beweis
  • E6 SQL-Ausbau · GROUP BY, SUM/COUNT/AVG, dann JOIN
  • E7 MVCC / Transaktionen (leicht) · konsistente Lesersicht während des Merge
  • E8 (optional) Persistenz · WAL + Snapshot: aus „Beweis" wird „echte DB"

Design-Entscheidungen (Mini-ADR)

Drei bewusste Weichen, an denen sich alles ausrichtet:

  1. Nordstern = HTAP-Beweis. Jede Etappe dient dem Ziel, Delta-Merge + Row/Column in einer Engine zu zeigen. Deshalb steht Delta-Merge bei E4, nicht am Ende.
  2. In-Memory zuerst. Authentisch zu HANA, schneller Erfolg. Platte ist optional (E8).
  3. Volles SQL als Fernziel. Start mit Mini-SQL (E2), bewusst auf GROUP BY/JOIN hingebaut (E6) denn ohne Aggregationen kann man die OLAP-Seite nicht vorführen.

Komplexitäts-Notiz fürs Gefühl: ein Full-Scan ist O(n), ein späterer Index-Lookup über einen B-Tree wäre O(\log n) der Unterschied, der OLTP erst schnell macht.


Repo-Features, die paulDB nutzt

Mein Gitea ist überraschend mächtig für dieses Projekt:

  • Projects (Kanban-Board) die Roadmap-Etappen als sichtbare Karten (To Do / In Progress / Done).
  • Milestones E0E8 als Meilensteine, gefüllt mit Issues.
  • Packages → Cargo-Registry 🦀 paulDB-Crates landen in meiner eigenen Rust-Registry statt auf crates.io.
  • Actions (CI/CD) bei jedem Push automatisch cargo build + cargo test.
  • Wiki Platz für tiefe Design-Docs jenseits dieses README.

Lernressourcen

Die Schultern, auf denen paulDB steht:

  • 📘 Database Internals Alex Petrov (Storage Engines, B-Trees, MVCC, WAL)
  • 📗 Crafting Interpreters Robert Nystrom (Tokenizer, AST fürs SQL-Frontend)
  • 💻 cstack/db_tutorial (eine DB Schritt für Schritt)
  • 🗃️ SQLite Source Code (das Vorbild für „klein, robust, vollständig")
  • 🦀 The Rust Programming Language („the Book") + Rust by Example
  • 🟧 SAP HANA Administration Guide (Delta-Merge & Column-Store-Kompression im Original)

Status

🌱 Vision-Phase. paulDB ist aktuell ein Zuhause für einen Traum, der bewusst wartet, bis mein SAP-Fundament steht. Das ist kein Rückstand das ist Reihenfolge.

Ein leeres Repo mit klarem Plan ist kein Hochstapeln. Es ist ein Versprechen an mich selbst.


von Paul Horn · gebaut, um zu verstehen · auf dem Weg nach BC 🏔️

S
Description
Eigene HTAP-Datenbank in Rust – Row+Column Storage, Delta-Merge (HANA-inspiriert). Langzeitprojekt 🦀🏳️‍🌈
Readme MIT 49 KiB