In-Memory-Datenbank nach dem Vorbild der SAP-HANA-Spaltenablage. Stack und Spezifikation.
GGS3-DB hält Tabellen spaltenweise im Arbeitsspeicher und spricht das PostgreSQL-Protokoll. Ausdrücke werden zu .NET-Code kompiliert, Auswertungen laufen parallel auf allen Kernen, und ein Commit ist erst nach fsync sichtbar.
In Entwicklung
Stand: Oktober 2026
Client
psql · Npgsql
PostgreSQL-Protokoll
SCRAM-SHA-256 · Extended Query
SQL-Compiler
Parser · Ausdrucksbäume
Spaltenablage im RAM
parallel auf allen Kernen
Redo-Log
Group Commit · fsync
Auf einen Blick
Die Eckwerte von GGS3-DB, belegt durch den Code.
PostgreSQL-Protokoll
psql und Npgsql verbinden sich ohne eigenen Treiber
Protokoll 3.0
Spalten im RAM
Wörterbuch-Kodierung für Text, parallele Scans auf allen Kernen
Spaltenablage
Commit nach fsync
Redo-Log mit Group Commit, Neustart aus Snapshot und Log
Dauerhaftigkeit
So läuft eine Abfrage
Vom Verbindungsaufbau bis zur Antwort, lesend und schreibend.
Verbinden
Verbinden
Jede Verbindung bekommt einen eigenen Thread. Die Anmeldung läuft über SCRAM-SHA-256, wie bei PostgreSQL.
Parsen
Parsen
Ein handgeschriebener Parser macht aus dem SQL einen Syntaxbaum, im Simple- oder Extended-Query-Protokoll.
Kompilieren
Kompilieren
Der Binder prüft Typen und Rechte und kompiliert Ausdrücke über Ausdrucksbäume zu .NET-Code. Bedingungen wandern zu den Tabellen, Gleichheits-Joins werden Hash-Joins.
Lesen
Lesen
Leser arbeiten auf einem unveränderlichen Stand und warten nie. Die Spalten werden in Blöcken parallel auf allen Kernen durchsucht.
Schreiben
Schreiben
Eine Transaktion zur Zeit hängt an die Spalten an. Der Commit landet im Redo-Log und ist erst nach fsync sichtbar; gleichzeitige Commits teilen sich ein fsync.
Antworten
Antworten
Die Zeilen gehen im Text- oder Binärformat zurück, wie bei PostgreSQL.
Der Stack
Eine .NET-Anwendung ohne Pakete außerhalb der Basisbibliothek, in sechs Teilen.
Protokoll
PostgreSQL-Protokoll 3.0 mit Simple und Extended Query, vorbereiteten Anweisungen und COPY in text, csv und binary.
SQL-Compiler
Handgeschriebener Parser und Binder; Ausdrücke werden über System.Linq.Expressions zu Delegates kompiliert.
Ausführung
Parallele Scans in Blöcken, Hash-Joins, nach Hash partitioniertes GROUP BY und ein Heap für Top-N.
Spaltenablage
Ein Array je Spalte, Wörterbuch-Kodierung für Text, Min/Max je Block und sortierte Hash-Läufe für Schlüssel.
Dauerhaftigkeit
Redo-Log mit CRC32C-Rahmen und Group Commit, Snapshots beim Checkpoint, Recovery aus Snapshot und Log.
Plattform
C# auf .NET 10 mit Server-GC; als Container auf einem Chiseled-Image ohne Shell.
Gemessen
Ein Vergleich mit PostgreSQL 18 per pgbench und ein Lasttest mit 50 Millionen Zeilen. Die Zahlen stammen aus dem Repository; Rohdaten sind nicht veröffentlicht.
Messbedingungen
- Docker Desktop auf einem M5 Max (18 Kerne, 64 GB), je Server 8 CPUs und 16 GB, fsync und synchronous_commit an.
- 2 Millionen Bestellungen und 100 000 Kunden; pgbench mit vorbereiteten Anweisungen aus postgres:18-alpine, im Netzwerk des Servers.
- PostgreSQL 18 getunt (shared_buffers 4 GB, work_mem 64 MB, parallele Worker), nach dem Laden VACUUM ANALYZE.
- Beide Server abwechselnd in zwei Runden, der bessere Wert zählt.
Vorbehalte
- Die Werte schwanken zwischen Läufen um 10 bis 40 %.
- Für Top-N liegen die Daten in Einfügereihenfolge, was GGS3-DB entgegenkommt; PostgreSQL hat keinen Index auf created.
- Angegeben sind Mittelwerte, keine Perzentile.
- Bei vielen gleichzeitig schreibenden Clients liegt PostgreSQL vorn.
Vergleich mit PostgreSQL 18
Transaktionen pro Sekunde, GGS3-DB / PostgreSQL (Faktor)
| Workload | 1 Client | 16 Clients | 64 Clients |
|---|---|---|---|
| WHERE id = $1 | 18 379 / 23 943 (0,77×) | 105 167 / 140 817 (0,75×) | 113 463 / 119 121 (0,95×) |
| count(*) WHERE customer_id = $1 | 1 128 / 64 (18×) | 1 495 / 135 (11×) | 1 474 / 145 (10×) |
| GROUP BY status | 111 / 22 (5,0×) | 107 / 42 (2,6×) | 109 / 42 (2,6×) |
| GROUP BY customer_id (100 000 Gruppen) | 24 / 2,5 (9,8×) | 32 / 7,6 (4,2×) | 30 / 7,1 (4,2×) |
| JOIN + GROUP BY country | 54 / 11 (4,7×) | 35 / 14 (2,5×) | 27 / 12 (2,3×) |
| ORDER BY created DESC LIMIT 20 | 386 / 22 (17×) | 1 817 / 44 (41×) | 1 578 / 44 (36×) |
| INSERT, ein Commit pro Zeile | 9 908 / 9 658 (1,03×) | 45 894 / 44 622 (1,03×) | 63 340 / 87 495 (0,72×) |
| UPDATE … WHERE id = $1 | 9 868 / 8 676 (1,14×) | 47 276 / 41 116 (1,15×) | 78 051 / 83 525 (0,93×) |
| 80 % Lesen, 20 % Update | 13 484 / 20 438 (0,66×) | 71 063 / 80 519 (0,88×) | 113 299 / 137 679 (0,82×) |
Lasttest mit 50 Millionen Zeilen
Im Prozess auf einem M5 Max (18 Kerne, 64 GB).
- COPY binary, 50 M Zeilen
- 11,3 s (4,4 M Zeilen/s)
- GROUP BY über 1000 Gruppen
- 0,3 s
- GROUP BY über eine Textspalte
- 0,3–0,6 s
- gefilterter count
- 0,05 s
- WHERE id = k, erster Zugriff (baut die Schlüssel-Läufe)
- 0,4 s
- JOIN mit 1000-Zeilen-Tabelle plus GROUP BY
- 0,2–0,4 s
- ORDER BY … LIMIT 10
- 0,3–0,4 s
- UPDATE von 5 M Zeilen
- 0,6 s
- Neustart (1,1 GB Snapshot, Index neu aufbauen)
- 1,7–1,9 s
Spalten im Arbeitsspeicher.
PostgreSQL-Protokoll ohne eigenen Treiber.
Sichtbar erst nach fsync.
Spezifikation
Speicher
- Spaltenablage
- ein Array je Spalte, Schreiben hängt nur an
- Text
- Wörterbuch-Kodierung, jede Zeile speichert nur einen int-Code
- Weitere Kompression
- keine
- UPDATE
- markiert die alte Zeile als gelöscht und hängt die neue Version an; der Checkpoint entfernt gelöschte Zeilen
- Schlüssel
- PRIMARY KEY und UNIQUE; die Suche nach einem Schlüssel läuft über sortierte Hash-Läufe je 16 384 Zeilen
- Min/Max je Block
- je 65 536 Zeilen, für ORDER BY … LIMIT
- Sekundärindizes
- nicht unterstützt
- Datenmenge
- alles im Arbeitsspeicher, höchstens rund 2,1 Milliarden Zeilen je Tabelle
Datentypen
- Zahlen
- smallint, integer, bigint, real, double precision, numeric(p,s) bis 28 Stellen
- Text
- text, varchar(n), UTF-8
- Zeit
- date, timestamp, timestamptz (UTC)
- Wahrheitswerte
- boolean
- Nicht unterstützt
- uuid, bytea, json, interval, Arrays, SERIAL
SQL
- Tabellen
- CREATE TABLE mit NOT NULL, PRIMARY KEY und UNIQUE; DROP TABLE
- Schreiben
- INSERT … VALUES | SELECT, UPDATE, DELETE, COPY … FROM STDIN in text, csv und binary
- Abfragen
- WHERE, INNER und LEFT JOIN, GROUP BY, HAVING, DISTINCT, ORDER BY, LIMIT/OFFSET
- Aggregate
- count, sum, avg, min, max, auch count(DISTINCT x)
- Ausdrücke
- CASE, CAST, IN, BETWEEN, LIKE/ILIKE, coalesce, date_trunc, extract und weitere Funktionen
- Rollen und Rechte
- CREATE, ALTER und DROP ROLE, GRANT und REVOKE je Tabelle, Besitzer
- Nicht unterstützt
- Unterabfragen, CTEs, UNION, Fensterfunktionen, RIGHT/FULL JOIN, ALTER TABLE, Views, Fremdschlüssel, RETURNING, ON CONFLICT, SAVEPOINT
Transaktionen
- Modell
- unveränderliche Versionen (Copy-on-Write) und eine Schreibsperre für die ganze Datenbank
- Leser
- arbeiten auf einem festen Stand und warten nie
- Schreiber
- eine Transaktion zur Zeit, andere warten bis zu 30 Sekunden auf die Sperre
- Isolation
- READ COMMITTED; REPEATABLE READ und SERIALIZABLE als Snapshot, ein Schreibversuch bricht ab, sobald seit dem Snapshot irgendein Commit kam
- Sperren je Tabelle
- statt einer Sperre für die ganze DatenbankGeplant
Dauerhaftigkeit
- Redo-Log
- Rahmen mit CRC32C-Prüfsumme, je Commit die ganze Transaktion
- Commit
- erst nach fsync sichtbar; gleichzeitige Commits teilen sich ein fsync (Group Commit)
- Checkpoint
- automatisch ab 1 GiB Log oder per CHECKPOINT; schreibt einen Snapshot ohne gelöschte Zeilen
- Neustart
- neuester Snapshot plus Log; eine unvollständige Transaktion am Ende wird verworfen
- Geprüft
- ein an jeder Byte-Position abgeschnittenes Log führt genau zum bestätigten Stand zurück
Protokoll und Clients
- Protokoll
- PostgreSQL-Protokoll 3.0, meldet sich als Version 16.0
- Abfragen
- Simple und Extended Query, vorbereitete Anweisungen, Text- und Binärformat
- Plan-Cache
- für vorbereitete INSERT, UPDATE und DELETE
- Plan-Cache für SELECT
- SELECT wird heute bei jeder Ausführung neu kompiliertGeplant
- COPY
- FROM STDIN in text, csv und binary
- Getestete Clients
- Npgsql (mit Server Compatibility Mode=NoTypeLoading), psql und pgbench
- Nicht unterstützt
- COPY TO, Abbrechen laufender Abfragen, LISTEN/NOTIFY, Systemkatalog (\d, DBeaver, pgAdmin)
Sicherheit
- Anmeldung
- SCRAM-SHA-256
- Rechte
- Rollen, Mitgliedschaften, Tabellenrechte auch für PUBLIC
- Root-Passwort
- aus einer Umgebungsvariable oder einer Datei, etwa einem Docker-Secret
- Ohne Passwort
- nimmt der Server nur Verbindungen über Loopback an
- Container
- Chiseled-Image ohne Shell, läuft nicht als root
- Verschlüsselung der Verbindung
- nicht unterstützt (kein TLS)
Betrieb
- Start
- eine .NET-Anwendung, per Kommandozeile oder Docker Compose
- Einstellungen
- Port, Adresse, Datenverzeichnis und Höchstzahl der Verbindungen
- Verbindungen
- ein Thread je Verbindung, standardmäßig bis zu 100
- Healthcheck
- im Docker-Image eingebaut, grün sobald die Recovery durch ist
- Metriken
- keine, nur Meldungen auf stdout und stderr
- Backup
- kein pg_dump und kein dokumentiertes Backup-Verfahren
Grenzen
- Passt gut
- Daten, die in den Arbeitsspeicher passen und vor allem ausgewertet werden: Filter ohne Index, GROUP BY, JOIN, Top-N
- Besser mit PostgreSQL
- viele gleichzeitig schreibende Clients, Daten größer als der Arbeitsspeicher, voller SQL-Umfang, TLS, Replikation, pg_dump
Lizenz
- Lizenz
- Apache 2.0
- Abhängigkeiten
- im Server keine Pakete außerhalb der .NET-Basisbibliothek
- Tests
- xunit.v3 und Npgsql
Stand
- Umgesetzt
- Spaltenablage, SQL-Teilmenge, Transaktionen, Redo-Log mit Recovery, Rollen und Rechte, PostgreSQL-Protokoll mit COPY, Docker-Image
- Tests
- 58 automatisierte Tests in 9 Bereichen, darunter Persistenz, Transaktionen, Protokoll und Rechte
- In Arbeit
- Ereignisschleifen statt eines Threads je Verbindung, ein Hash-Index für Schlüssel und weitere OLTP-Optimierungen
- Geplant
- Plan-Cache für SELECT, Sperren je Tabelle
Häufige Fragen zu GGS3-DB
Kurz beantwortet, die Details stehen in der Spezifikation.
Ebenfalls auf GGS Network
Die übrigen Seiten, kurz erklärt.
Remote-Desktop
Stack und Spezifikation des Remote-Desktops auf Basis von Moonlight und Sunshine.
Team
Wer GGS Network entwirft, baut und betreibt.
Status
Ob Website, API und Dienste gerade erreichbar sind, laufend gemessen.