Files
paulDB/README.md
T
2026-06-12 22:51:32 +02:00

198 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
include_toc: true
---
# 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:
```mermaid
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.
```mermaid
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](https://cstack.github.io/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.
---
<sub>von Paul Horn · gebaut, um zu verstehen · auf dem Weg nach BC 🏔️</sub>