199 lines
8.1 KiB
Markdown
199 lines
8.1 KiB
Markdown
---
|
||
include_toc: true
|
||
gitea: none
|
||
---
|
||
|
||
# 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** – E0–E8 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> |