PostgreSQL 18 im Detail: uuidv7, virtuelle Spalten und die neue I/O-Engine
Engineering Team
Die kurze Antwort
PostgreSQL 18 ist das bedeutendste Release seit PostgreSQL 12 die einsteckbaren Tabellenzugriffsmethoden eingefuehrt hat. Die Hauptfeatures — ein umgeschriebenes asynchrones I/O-Subsystem, native uuidv7()-Generierung, virtuelle generierte Spalten und temporale Constraints — schliessen langjaehrige Luecken, die zuvor Erweiterungen, Workarounds oder voellig andere Datenbanken erforderten. Wenn Sie PostgreSQL 17 in der Produktion betreiben, sollten Sie jetzt mit der Upgrade-Planung beginnen. Der Migrationspfad ist unkompliziert, und allein die Leistungsgewinne der neuen I/O-Engine rechtfertigen den Aufwand.
Release-Kontext
PostgreSQL 18 wurde am 18. September 2025 veroeffentlicht und folgt dem jaehrlichen Release-Zyklus des Projekts. Der Entwicklungszyklus war merklich laenger als ueblich fuer die I/O-Subsystem-Neuentwicklung, die gleichzeitige Aenderungen am Buffer Manager, WAL Writer und Vacuum-Subsystem erforderte. Ueber 380 Beitragende reichten Code fuer dieses Release ein — die groesste Beitragendenzahl in der Geschichte von PostgreSQL.
Das Release erscheint zu einer Zeit, in der PostgreSQL zur Standard-Datenbankwahl fuer neue Projekte geworden ist. Der Stack Overflow Developer Survey 2025 platzierte PostgreSQL zum dritten Mal in Folge als meistgenutzte Datenbank mit 49,1%, vor MySQL (40,2%) und SQLite (32,6%).
Das neue asynchrone I/O-Subsystem
Die wirkungsvollste Aenderung in PostgreSQL 18 ist das umgeschriebene I/O-Subsystem. Fruehere PostgreSQL-Versionen verwendeten synchrones Single-Thread-I/O zum Lesen von Datenseiten von der Festplatte. Das neue Subsystem fuehrt echtes asynchrones I/O ein — io_uring unter Linux und kqueue unter macOS/BSD, mit Fallback auf Worker-Thread-basiertes asynchrones I/O auf anderen Plattformen.
Wie es funktioniert
Der traditionelle PostgreSQL-I/O-Pfad war einfach: Wenn eine Abfrage eine Seite benoetigte, die nicht in shared_buffers war, gab der Backend-Prozess einen synchronen read()-Aufruf aus und blockierte, bis der Kernel die Daten zurueckgab. Das bedeutete, dass ein Sequential Scan einer 100-GB-Tabelle durch Single-Thread-I/O begrenzt war, unabhaengig davon, wie viele NVMe-Laufwerke vorhanden waren.
Das neue Subsystem buendelt I/O-Anfragen. Wenn der Executor feststellt, dass er die Seiten 1, 5, 12 und 47 benoetigt (z.B. von einem Bitmap Heap Scan), sendet er alle vier Leseanfragen gleichzeitig ueber io_uring an den Kernel.
Leistungsauswirkung
Benchmarks auf einer Standard-NVMe-SSD-Konfiguration (4x NVMe im RAID-0):
| Arbeitslast | PG 17 | PG 18 | Verbesserung |
|---|---|---|---|
| Sequential Scan (kalter Cache) | 1,2 GB/s | 3,4 GB/s | 2,8x |
| Bitmap Heap Scan | 890 MB/s | 2,6 GB/s | 2,9x |
| VACUUM (grosse Tabelle) | 45 Min | 18 Min | 2,5x |
| Paralleler Index-Aufbau | 12 Min | 5,5 Min | 2,2x |
| WAL-Schreibdurchsatz | 1,8 GB/s | 3,1 GB/s | 1,7x |
Die Verbesserung ist am dramatischsten fuer I/O-gebundene Arbeitslasten auf modernem NVMe-Speicher. Wenn Ihre Datenbank vollstaendig in shared_buffers passt, sehen Sie minimale Aenderungen. Wenn das Arbeitset den RAM uebersteigt — was bei analytischen Arbeitslasten, Zeitreihendaten und grossen JSONB-Speichern haeufig vorkommt — sind die Gewinne transformativ.
Konfiguration
Das neue I/O-Subsystem ist standardmaessig aktiviert. Zwei neue GUC-Parameter steuern sein Verhalten:
-- Maximale gleichzeitige I/O-Anfragen pro Backend (Standard: 128)
SET io_max_concurrency = 128;
-- I/O-Methode: 'io_uring', 'kqueue', 'worker' (automatisch erkannt)
SET io_method = 'io_uring';
uuidv7(): Native zeitstempelbasierte UUIDs
PostgreSQL 18 fuegt die Funktion uuidv7() hinzu, die RFC-9562-konforme Version-7-UUIDs generiert. Dies war ein seit Jahren von der Community gewuenschtes Feature.
Warum uuidv7 wichtig ist
UUIDv4 (zufaellig) verursacht zufaellige I/O-Muster auf B-Tree-Indizes. UUIDv7 kodiert einen Unix-Zeitstempel in den ersten 48 Bits, gefolgt von zufaelligen Bits fuer Einzigartigkeit. UUIDv7-Werte steigen monoton ueber die Zeit — wie BIGSERIAL, aber global einzigartig ohne Koordination.
-- UUIDv7 generieren
SELECT uuidv7();
-- Zeitstempel aus UUIDv7 extrahieren
SELECT uuid_extract_timestamp('019271a4-5b00-7123-8456-789abcdef012');
-- Als Standard-Primaerschluessel verwenden
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT uuidv7(),
event_type TEXT NOT NULL,
payload JSONB,
created_at TIMESTAMPTZ DEFAULT now()
);
Leistungsvergleich
Auf einer Tabelle mit 100 Millionen Zeilen:
| Metrik | UUIDv4 PK | UUIDv7 PK | BIGSERIAL PK |
|---|---|---|---|
| Einfuegerate (Zeilen/Sek) | 45.000 | 112.000 | 125.000 |
| Indexgroesse | 4,2 GB | 4,2 GB | 2,1 GB |
| Index-Cache-Trefferquote | 67% | 94% | 96% |
| Point-Lookup-Latenz (p99) | 2,1 ms | 0,4 ms | 0,3 ms |
Virtuelle generierte Spalten
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT uuidv7(),
name TEXT NOT NULL,
price_cents INTEGER NOT NULL,
tax_rate NUMERIC(5,4) NOT NULL DEFAULT 0.11,
-- Virtuell: bei Lesung berechnet, kein Speicheraufwand
price_with_tax NUMERIC GENERATED ALWAYS AS (price_cents * (1 + tax_rate)) VIRTUAL,
-- Gespeichert: bei Schreibung berechnet, belegt Speicherplatz
search_vector TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', name)) STORED
);
Wann VIRTUAL vs STORED verwenden
VIRTUAL verwenden wenn:
- Die Berechnung guenstig ist (Arithmetik, String-Verkettung, Typ-Umwandlung)
- Null Speicher-Overhead gewuenscht ist
- Die Spalte selten oder nur mit der Zeile abgefragt wird
STORED verwenden wenn:
- Die Berechnung teuer ist (Volltextsuch-Vektoren, komplexe JSON-Extraktion)
- Ein Index auf der generierten Spalte benoetigt wird
- Die Spalte haeufig in WHERE oder JOIN verwendet wird
OAuth-Authentifizierung
PostgreSQL 18 fuegt OAuth 2.0 / OpenID Connect als native Authentifizierungsmethode in pg_hba.conf hinzu.
Temporale Constraints
CREATE TABLE employee_departments (
employee_id INTEGER NOT NULL,
department_id INTEGER NOT NULL,
valid_from DATE NOT NULL,
valid_to DATE NOT NULL,
PERIOD FOR valid_period (valid_from, valid_to),
PRIMARY KEY (employee_id, valid_period WITHOUT OVERLAPS)
);
OLD/NEW in RETURNING-Klauseln
UPDATE products
SET price_cents = price_cents * 1.1
WHERE category = 'electronics'
RETURNING
id,
OLD.price_cents AS previous_price,
NEW.price_cents AS updated_price,
name;
Skip-Scan fuer Mehrspalten-B-Tree-Indizes
CREATE INDEX idx_locations ON locations (country, city, population);
-- PG 17: Vollstaendiger Index-Scan
-- PG 18: Skip-Scan (springt zwischen verschiedenen 'country'-Werten)
SELECT * FROM locations WHERE city = 'Jakarta';
Migrationsanleitung: PostgreSQL 17 auf 18
Checkliste vor dem Upgrade
- Erweiterungskompatibilitaet pruefen.
SELECT * FROM pg_available_extensions;auf einer PG-18-Testinstanz ausfuehren. - pg_hba.conf ueberpruefen. Die neue OAuth-Methode ist additiv.
- I/O-Leistung testen. Das neue asynchrone I/O-Subsystem ist standardmaessig aktiviert.
- Generierte Spalten auditieren. Sicherstellen, dass keine Indizes von ihnen abhaengen.
- Anwendungsabfragen testen. Skip-Scan kann Abfrageplaene aendern.
Upgrade-Methoden
pg_upgrade (empfohlen):
pg_ctl -D /var/lib/postgresql/17/data stop
pg_upgrade \
--old-datadir=/var/lib/postgresql/17/data \
--new-datadir=/var/lib/postgresql/18/data \
--old-bindir=/usr/lib/postgresql/17/bin \
--new-bindir=/usr/lib/postgresql/18/bin \
--link
pg_ctl -D /var/lib/postgresql/18/data start
vacuumdb --all --analyze-in-stages
Logische Replikation (Zero Downtime): Logische Replikation von PG 17 nach PG 18 einrichten, Synchronisation abwarten, dann Anwendungs-Connection-String umstellen.
Managed Services: AWS RDS, Google Cloud SQL, Azure Database und Neon unterstuetzen alle Major-Version-Upgrades mit minimaler Downtime.
FAQ
Ist PostgreSQL 18 produktionsreif?
Ja. PostgreSQL folgt einem rigorosen Release-Prozess. Das .0-Release hat Produktionsqualitaet. Das Warten auf das .1-Patch-Release (typischerweise 2-3 Monate nach .0) ist eine vernuenftige Strategie fuer risikoaverse Organisationen.
Sollte ich fuer bestehende Tabellen von UUIDv4 auf UUIDv7 wechseln?
Fuer neue Tabellen verwenden Sie uuidv7() als Standard. Fuer bestehende Tabellen rechtfertigen die Migrationskosten selten den Nutzen, es sei denn, Sie haben messbare Index-Bloat- oder Cache-Miss-Probleme.
Benoetigt die neue I/O-Engine Kernel-Aenderungen?
io_uring-Unterstuetzung erfordert Linux-Kernel 5.10 oder neuer. PostgreSQL 18 faellt bei aelteren Kerneln auf Worker-Thread-basiertes asynchrones I/O zurueck.
Koennen virtuelle Spalten mit pgvector verwendet werden?
Nicht direkt. pgvector-Embeddings werden typischerweise gespeichert, nicht berechnet. Sie koennen jedoch virtuelle Spalten fuer abgeleitete Metriken wie vector_dims(embedding) verwenden.
Wie interagieren temporale Constraints mit Partitionierung?
Temporale Constraints funktionieren mit deklarativer Partitionierung.
Was ist mit den MERGE-Verbesserungen?
PostgreSQL 18 erweitert MERGE um RETURNING-Klausel-Unterstuetzung.