PostgreSQL® Archiv - credativ®

PostgreSQL ist eine objektrelationale Open-Source-Datenbank, die seit 1986 entwickelt wird. Sie zeichnet sich durch ACID-Compliance, hohe Erweiterbarkeit und starke SQL-Unterstützung aus. Unternehmen schätzen PostgreSQL wegen seiner Zuverlässigkeit, Performance und Kostenfreiheit. Diese Anleitung beantwortet die wichtigsten Fragen zur Installation, Konfiguration und dem Einsatz von PostgreSQL im Unternehmensumfeld.

Was ist PostgreSQL und warum nutzen es so viele Unternehmen?

Slonik, the PostgreSQL mascot.

PostgreSQL ist ein objektrelationales Datenbankmanagementsystem, das als Open-Source-Software kostenlos verfügbar ist. Die Datenbank entstand aus dem POSTGRES-Projekt der University of California und wird seit 1986 kontinuierlich weiterentwickelt. PostgreSQL unterstützt sowohl relationale als auch nicht relationale Datenstrukturen und gilt als eine der fortschrittlichsten Open-Source-Datenbanken weltweit.

Die wichtigsten Merkmale von PostgreSQL umfassen ACID-Compliance für Transaktionssicherheit, Multi-Version Concurrency Control (MVCC) für gleichzeitige Zugriffe und umfassende SQL-Unterstützung. Die Datenbanksoftware bietet außerdem erweiterte Datentypen wie JSON, XML und Arrays sowie die Möglichkeit, eigene Funktionen und Datentypen zu definieren.

Unternehmen verschiedener Größen setzen auf PostgreSQL, weil es eine stabile und skalierbare Lösung für komplexe Anwendungen bietet. Die aktive Entwicklergemeinschaft sorgt für regelmäßige Updates und Sicherheitspatches. Zusätzlich ermöglicht die freie Lizenz den Einsatz ohne Lizenzkosten, was besonders für wachsende Unternehmen attraktiv ist.

Wie unterscheidet sich PostgreSQL von anderen Datenbanken wie MySQL?

PostgreSQL und MySQL unterscheiden sich in mehreren wichtigen Bereichen, wobei beide ihre spezifischen Stärken haben. PostgreSQL bietet erweiterte Datentypen wie JSON, JSONB, Arrays und geometrische Typen, während MySQL sich auf grundlegende SQL-Datentypen konzentriert. Bei komplexen Abfragen und Joins zeigt PostgreSQL oft eine bessere Performance, MySQL hingegen ist bei einfachen Leseoperationen häufig schneller.

Die Lizenzierung unterscheidet sich ebenfalls: PostgreSQL steht unter der PostgreSQL-Lizenz, die ähnlich der BSD-Lizenz sehr permissiv ist. MySQL verwendet eine duale Lizenzierung mit GPL für Open-Source-Projekte und kommerziellen Lizenzen für proprietäre Software. Dies kann bei der Entscheidung für kommerzielle Anwendungen relevant werden.

Beide Datenbanken haben starke Communities und professionellen Support verfügbar.

Welche Vorteile bietet PostgreSQL für Unternehmen?

PostgreSQL bietet Unternehmen erhebliche Kostenvorteile durch die Open-Source-Lizenz, da keine Lizenzgebühren anfallen. Dies ermöglicht es auch kleinen und mittleren Unternehmen, eine professionelle Datenbankinfrastruktur aufzubauen. Die eingesparten Lizenzkosten können in Hardware, Entwicklung oder professionellen Support investiert werden.

Die Sicherheitsstandards von PostgreSQL entsprechen Enterprise-Anforderungen mit Features wie Row-Level-Security, SSL-Verschlüsselung und umfassenden Authentifizierungsmöglichkeiten. Die Datenbank unterstützt verschiedene Compliance-Standards und bietet detaillierte Audit-Funktionen für regulierte Branchen.

PostgreSQL skaliert sowohl vertikal als auch horizontal und wächst mit den Anforderungen Ihres Unternehmens mit. Die Unabhängigkeit von einzelnen Herstellern verhindert Vendor-Lock-in und gibt Ihnen die Flexibilität, Support und Services von verschiedenen Anbietern zu beziehen. Die große Community und das offene Entwicklungsmodell sorgen für kontinuierliche Innovation und langfristige Verfügbarkeit.

Wie installiert und konfiguriert man PostgreSQL richtig? Einfaches Beispiel:

Die PostgreSQL-Installation erfolgt je nach Betriebssystem auf verschiedenen Wegen. Unter Ubuntu/Debian verwenden Sie beispielsweise den Befehl sudo apt-get install postgresql postgresql-contrib. Für Windows laden Sie den offiziellen Installer von postgresql.org herunter. macOS-Nutzer können PostgreSQL über Homebrew mit brew install postgresql installieren.

Nach der Installation müssen Sie den PostgreSQL-Dienst starten und einen Datenbankbenutzer anlegen. Unter Linux erfolgt dies mit sudo systemctl start postgresql und sudo -u postgres createuser --interactive. Die grundlegende Konfiguration erfolgt über die Dateien postgresql.conf für allgemeine Einstellungen und pg_hba.conf für die Authentifizierung.

Damit ist man auf den meisten Linux-Distributionen bereits sofort für einen Test einsatzbereit. Für anspruchsvollere Anwendungen sollte man natürlich weitere Überlegungen treffen, etwa, ob man sich auf die mit der jeweiligen Distribution gelieferten Pakete und Versionen verlassen möchte oder ob man sich auf die Pakete von PostgreSQL.org konzentriert. Auch gehören zu einem performanten Datenbankserver Überlegungen zum darunter liegenden Dateisystem sowie dessen Optimierung.

Für den produktiven Einsatz sollten Sie ferner wichtige Sicherheitseinstellungen vornehmen:

Wie credativ® bei PostgreSQL-Projekten unterstützt

credativ® bietet umfassenden PostgreSQL-Support für Unternehmen, die eine professionelle Betreuung ihrer Datenbankinfrastruktur benötigen. Unser Service umfasst 24/7-Support durch erfahrene PostgreSQL-Spezialisten, die bei kritischen Problemen sofort verfügbar sind. Sie erhalten direkten Zugang zu unserem deutschen Support-Team ohne Umwege über internationale Callcenter. Wir sind gerne für Sie da und unterstützen schon bei der Auswahl von Open-Source Tools.

Unsere PostgreSQL-Services im Detail:

Als PostgreSQL-Experten mit über 20 Jahren Erfahrung helfen wir Ihnen dabei, das volle Potenzial Ihrer Datenbank auszuschöpfen. Kontaktieren Sie uns für eine unverbindliche Beratung zu Ihrem PostgreSQL-Projekt und erfahren Sie, wie wir Ihre Datenbankinfrastruktur optimieren können.

 

Postgres, PostgreSQL und das Slonik-Logo sind Marken oder eingetragene Marken der PostgreSQL Community Association of Canada und werden mit deren Genehmigung verwendet.

KI-generiertes Bild, das eine asynchron laufende Datenbank symbolisiertPostgreSQL 17 führte Streaming-E/A ein – das Gruppieren mehrerer Seitenlesevorgänge in einen einzigen Systemaufruf und die Verwendung intelligenterer posix_fadvise()-Hinweise. Allein das führte in einigen Workloads zu bis zu ~30 % schnelleren sequenziellen Scans, aber es war immer noch strikt synchron: Jeder Backend-Prozess führte einen Lesevorgang aus und wartete dann darauf, dass der Kernel Daten zurückgab, bevor er fortfuhr. Vor PG17 las PostgreSQL typischerweise eine 8-kB-Seite gleichzeitig.

PostgreSQL 18 geht den nächsten logischen Schritt: ein vollständiges asynchrones E/A-Subsystem (AIO), das mehrere Lesevorgänge gleichzeitig ausführen kann, während die Backends weiterhin nützliche Arbeit leisten. Lesevorgänge werden überlappend statt nur serialisiert. Das AIO-Subsystem ist gezielt auf Operationen ausgerichtet, die ihre zukünftigen Blocknummern im Voraus kennen und mehrere Lesevorgänge im Voraus ausführen können:

Autovacuum profitiert ebenfalls von dieser Änderung, da seine Worker dieselben VACUUM/ANALYZE-Codepfade verwenden. Andere Operationen bleiben vorerst synchron:

Es wird erwartet, dass zukünftige Arbeiten die Abdeckung erweitern, insbesondere Index‑Only-Scans und einige Optimierungen des Schreibpfads.

Signifikante Verbesserungen für Cloud-Volumes

Community-Benchmarks zeigen, dass PostgreSQL 18 AIO die Kaltcache-Datenlesevorgänge in Cloud-Setups mit netzwerkgebundenem Speicher, bei denen die Latenz hoch ist, deutlich verbessert. Die AWS-Dokumentation besagt, dass die durchschnittliche Latenz von Block Express-Volumes „unter 500 Mikrosekunden für 16 KiB E/A-Größe“ liegt, während die Latenz von General Purpose-Volumes 800 Mikrosekunden überschreiten kann. Einige Artikel legen nahe, dass unter hoher Last jeder physische Block, der von der Festplatte gelesen wird, etwa 1 ms kosten kann, während die Seitenverarbeitung in PostgreSQL viel günstiger ist. Indem wir viele Seiten in einem Lesevorgang kombinieren, kosten alle diese Seiten zusammen jetzt etwa 1 ms. Und indem wir mehrere Leseanforderungen gleichzeitig parallel ausführen, zahlen wir diese 1 ms Latenz effektiv nur einmal pro Batch.

Asynchrone E/A-Methoden

Das neue Subsystem kann in einem von drei Modi ausgeführt werden, die über den Parameter io_method mit den möglichen Werten „worker“ (Standard), „io_uring“ und „sync“ konfiguriert werden. Wir werden erläutern, wie die einzelnen Modi funktionieren, und dann zeigen, wie die asynchrone E/A in unserer Umgebung überwacht werden kann.

io_method = sync

Dieser Modus schaltet AIO effektiv aus. Lesevorgänge werden über dieselbe AIO-API, aber synchron ausgeführt, wobei reguläre preadv- oder pwritev-Methoden auf dem Backend-Prozess verwendet werden, der die E/A ausgegeben hat. Diese Methode verwendet keinen zusätzlichen gemeinsam genutzten Speicher und ist hauptsächlich für Regressionstests gedacht oder wenn wir vermuten, dass sich AIO falsch verhält. Sie wird auch intern als Fallback auf die synchrone E/A für Operationen verwendet, die keine asynchrone E/A verwenden können. PostgreSQL-Kernfunktionen geben einen Fehler aus, wenn eine Erweiterung versuchen würde, die asynchrone E/A über die AIO-API zu erzwingen, wenn die globale io_method auf „sync“ gesetzt ist. Verfügbare Benchmarks zeigen, dass dieser PostgreSQL 18-Modus ähnlich wie die Streaming-E/A von PostgreSQL 17 funktioniert.

io_method = io_uring (Linux only)
Auf modernen Linux-Systemen (Kernel-Version 5.1 oder höher) kann PostgreSQL direkt mit der io_uring-Schnittstelle des Kernels kommunizieren. Die Verwendung erfordert, dass PostgreSQL mit liburing-Unterstützung gebaut wird – wir können dies in PostgreSQL mit der Funktion select from pg_config() überprüfen:
SELECT pg_config FROM pg_config() where pg_config::text ilike ’%liburing%’;
Asynchrone E/A-Operationen von PostgreSQL (sowohl io_uring als auch Worker) verwenden gemeinsam genutzte Speicherstrukturen, um die Anforderungen auszugeben und Informationen über deren Abschluss oder Fehler zu empfangen. Auf diese Weise kann der PostgreSQL AIO-Code Batching und Parallelität verwalten, ohne direkt von einer bestimmten AIO-Methode abhängig zu sein. Der PostgreSQL-Code verwaltet eine separate io_uring-Instanz für jedes Backend, einschließlich Hilfsprozesse. Die Ringe werden jedoch im Postmaster erstellt, sodass sie gemeinsam genutzten Speicher verwenden können und es keine Konflikte oder Blockierungen zwischen den Backends gibt.
Das Verarbeitungsszenario ist sehr einfach:
  1. Backends schreiben Anforderungen über die API in einen Submission-Ring im gemeinsam genutzten Speicher
  2. Der Kernel führt E/A asynchron aus und schreibt Ergebnisse in einen Completion-Ring
  3. Der Inhalt des Completion-Rings wird vom Backend mit weniger Kontextwechseln verarbeitet

Die Ausführung erfolgt weiterhin im selben Prozess wie bei der Methode „sync„, aber es werden Kernel-Worker-Threads für die parallele Verarbeitung verwendet. Dies ist typischerweise bei sehr schnellen NVMe-SSDs von Vorteil.

Die io_uring-Linux-Funktion hatte jedoch auch eine schwierige Sicherheitshistorie. Sie umgeht traditionelle Syscall-Audit-Pfade und war daher an einem großen Teil der Linux-Kernel-Exploits beteiligt. Google berichtete, dass 60 % der Linux-Kernel-Schwachstellen im Jahr 2022 io_uring betrafen und einige Sicherheitstools diese Art von Angriffen nicht aufdecken konnten. Daher deaktivieren einige Containerumgebungen io_uring vollständig.

io_method = worker

Dies ist die plattformübergreifende, „sichere“ Implementierung und der Standard in PostgreSQL 18. Der Mechanismus ist dem bestehenden parallelen Abfrageverarbeitung sehr ähnlich. Der Hauptunterschied besteht darin, dass Hintergrund-E/A-Worker langlebige, unabhängige Prozesse sind, die beim Serverstart erstellt werden, nicht kurzlebige Prozesse, die pro Abfrage erzeugt werden.

Typischer Ablauf:
  1. Beim Serverstart erstellt der Postmaster einen Pool von E/A-Worker-Prozessen. Die Anzahl wird durch den Parameter io_workers mit einem Standardwert von 3 gesteuert. Benchmarks legen jedoch nahe, dass diese Zahl auf vielen Kernmaschinen höher sein sollte, typischerweise zwischen ¼ und ½ der verfügbaren CPU-Threads. Der beste Wert hängt von der Workload und der Speicherlatenz ab.
  2. Backends übermitteln Leseanforderungen in eine gemeinsam genutzte Speicher-Submission-Queue. Diese Submission-Queue ist im Allgemeinen ein Ringpuffer, in den mehrere Backends gleichzeitig schreiben können. Sie enthält nur Metadaten über die Anforderung – Handle-Indizes, nicht den vollständigen Anforderungsdatensatz. Es gibt nur eine Submission-Queue für den gesamten Cluster, nicht pro Datenbank oder pro Backend. Die tatsächlichen Details der Anforderung werden in einer separaten Speicherstruktur gespeichert.
  3. Es wird geprüft, ob die Anforderung synchron ausgeführt werden muss oder asynchron verarbeitet werden kann. Die synchrone Ausführung kann auch gewählt werden, wenn die Submission-Queue voll ist. Dies vermeidet Probleme mit der gemeinsam genutzten Speichernutzung unter extremer Last. Im Falle einer synchronen Ausführung verwendet der Code den Pfad für die oben beschriebene „sync“-Methode.
  4. Die Anforderungsübermittlung im gemeinsam genutzten Speicher weckt einen E/A-Worker auf, der die Anforderung abruft und traditionelle blockierende read() / pread()-Aufrufe ausführt. Wenn die Queue noch nicht leer ist, kann der aufgeweckte Worker 2 zusätzliche Worker aufwecken, um sie parallel zu verarbeiten. Im Code wird erwähnt, dass dies in Zukunft auf konfigurierbare N Worker erweitert werden kann. Dieses Limit hilft, das sogenannte „Thundering Herd Problem“ zu vermeiden, bei dem ein einzelner Submitter zu viele Worker aufwecken würde, was zu Chaos und Sperren für andere Backends führen würde.
  5. Eine Einschränkung für die asynchrone E/A ist die Tatsache, dass Worker nicht einfach von Backends geöffnete Dateideskriptoren wiederverwenden können, sondern Dateien in ihrem eigenen Kontext neu öffnen müssen. Wenn dies für einige Arten von Operationen nicht möglich ist, wird für diese spezifische Anforderung der synchrone E/A-Pfad verwendet.
  6. Wenn Worker eine Anforderung ohne Fehler abschließen, schreiben sie Datenblöcke in gemeinsam genutzte Puffer, legen das Ergebnis in eine Completion-Queue und signalisieren das Backend.
  7. Aus der Perspektive des Backends wird die E/A „asynchron“, da das „Warten“ in Worker-Prozessen stattfindet, nicht im Abfrageprozess selbst.
Vorteile dieses Ansatzes:

Tuning der neuen E/A-Parameter

PostgreSQL 18 fügt mehrere Parameter im Zusammenhang mit Festplatten-E/A hinzu oder aktualisiert sie. Wir haben bereits io_method und io_workers behandelt; sehen wir uns die anderen an. Weitere neue Parameter sind io_combine_limit und io_max_combine_limit. Sie steuern, wie viele Datenseiten PostgreSQL in einer einzigen AIO-Anforderung gruppiert. Größere Anforderungen führen typischerweise zu einem besseren Durchsatz, können aber auch die Latenz und die Speichernutzung erhöhen. Werte ohne Einheiten werden in 8-kB-Datenblöcken interpretiert. Mit Einheiten (kB, MB) stellen sie direkt die Größe dar – sollten jedoch Vielfache von 8 kB sein.

Der Parameter io_max_combine_limit ist eine feste Obergrenze beim Serverstart, io_combine_limit ist der vom Benutzer einstellbare Wert, der zur Laufzeit geändert werden kann, aber das Maximum nicht überschreiten darf. Die Standardwerte für beide sind 128 kB (16 Datenseiten). Die Dokumentation empfiehlt jedoch, unter Unix bis zu 1 MB (128 Datenseiten) und unter Windows 128 kB (16 Datenseiten – aufgrund von Einschränkungen in internen Windows-Puffern) einzustellen. Wir können mit höheren Werten experimentieren, aber basierend auf HW- und OS-Limits erreichen die AIO-Vorteile nach einer bestimmten Chunk-Größe ein Plateau; ein zu hohes Einstellen hilft nicht und kann sogar die Latenz erhöhen.

PostgreSQL 18 führt auch die Einstellung io_max_concurrency ein, die die maximale Anzahl von E/As steuert, die ein Prozess gleichzeitig ausführen kann. Die Standardeinstellung -1 bedeutet, dass der Wert automatisch basierend auf anderen Einstellungen ausgewählt wird, aber er darf 64 nicht überschreiten.

Ein weiterer verwandter Parameter ist effective_io_concurrency – die Anzahl der gleichzeitigen E/A-Operationen, die gleichzeitig auf dem Speicher ausgeführt werden können. Der Wertebereich liegt zwischen 1 und 1000, der Wert 0 deaktiviert asynchrone E/A-Anforderungen. Der Standardwert ist jetzt 16, einige Community-Artikel empfehlen, auf modernen SSDs bis zu 200 zu gehen. Die beste Einstellung hängt von der spezifischen Hardware und dem Betriebssystem ab, einige Artikel warnen jedoch auch davor, dass ein zu hoher Wert die E/A-Latenz für alle Abfragen erheblich erhöhen kann.

So überwachen Sie asynchrone E/A

pg_stat_activity
Für io_method = worker sind Hintergrund-E/A-Worker in pg_stat_activity als backend_type = ‚io worker‘ sichtbar. Sie zeigen die Werte wait_event_type / wait_event Activity / IoWorkerMain an, wenn sie im Leerlauf sind, oder typischerweise IO / DataFileRead, wenn sie mit der Arbeit beschäftigt sind.
SELECT pid, backend_start, wait_event_type, wait_event, backend_type
FROM pg_stat_activity
WHERE backend_type = 'io worker';


  pid | backend_start.                | wait_event_type | wait_event   | backend_type
------+-------------------------------+-----------------+--------------+--------------
   34 | 2025-12-09 11:44:23.852461+00 | Activity        | IoWorkerMain | io worker
   35 | 2025-12-09 11:44:23.852832+00 | Activity        | IoWorkerMain | io worker
   36 | 2025-12-09 11:44:23.853119+00 | IO              | DataFileRead | io worker
   37 | 2025-12-09 11:44:23.8534+00   | IO              | DataFileRead | io worker
Wir können pg_stat_io mit pg_stat_activity kombinieren, um zu sehen, welche Backends AIO-Anforderungen ausgeben, welche Abfragen sie ausführen und wie ihr aktueller AIO-Status ist:
SELECT a.pid, a.usename, a.application_name, a.backend_type, a.state, a.query,
ai.operation, ai.state AS aio_state, ai.length AS aio_bytes, ai.target_desc
FROM pg_aios ai
JOIN pg_stat_activity a ON a.pid = ai.pid
ORDER BY a.backend_type, a.pid, ai.io_id;


 -[ RECORD 1 ]----+------------------------------------------------------------------------
 pid              | 58
 usename          | postgres
 application_name | psql
 backend_type     | client backend
 state            | active
 query.           | explain analyze SELECT ........
 operation        | readv
 aio_state        | SUBMITTED
 aio_bytes        | 704512
 target_desc      | blocks 539820..539905 in file "pg_tblspc/16647/PG_18_202506291/5/16716"
 -[ RECORD 2 ]----+------------------------------------------------------------------------
 pid              | 159
 usename          | postgres
 application_name | psql
 backend_type     | parallel worker
 state            | active
 query            | explain analyze SELECT ........
 operation        | readv
 aio_state        | SUBMITTED
 aio_bytes        | 704512
 target_desc      | blocks 536326..536411 in file "pg_tblspc/16647/PG_18_202506291/5/16716"

pg_aios: Current AIO handles
PostgreSQL 18 führt mehrere neue Beobachtungsfunktionen ein, die uns helfen, die asynchrone E/A in Aktion zu überwachen. Die neue Systemansicht pg_aios listet aktuell verwendete asynchrone E/A-Handles auf – im Wesentlichen „E/A-Anforderungen, die vorbereitet, ausgeführt oder abgeschlossen werden“.
Die wichtigsten Spalten für jedes Handle sind:
Wir können einige einfache Statistiken aller aktuell laufenden E/As generieren, gruppiert nach Status und Ergebnis:
-- Zusammenfassung der aktuellen AIO-Handles nach Status und Ergebnis
SELECT state, result, count(*) AS cnt, pg_size_pretty(sum(length)) AS total_size
FROM pg_aios GROUP BY state, result ORDER BY state, result;

 state            | result  | cnt | total_size
------------------+---------+-----+------------
 COMPLETED_SHARED | OK      | 1   | 688 kB
 SUBMITTED        | UNKNOWN | 6   | 728 kB

-- In-flight async I/O handles
SELECT COUNT(*) AS aio_handles, SUM(length) AS aio_bytes FROM pg_aios;

 aio_handles | aio_bytes
-------------+-----------
   7         | 57344

-- Sessions currently waiting on I/O
SELECT COUNT(*) AS sessions_waiting_on_io FROM pg_stat_activity WHERE wait_event_type = 'IO';

 sessions_waiting_on_io
------------------------
  9
Oder wir können es verwenden, um Details zu aktuellen AIO-Anforderungen anzuzeigen:
SELECT pid, state, operation, pg_size_pretty(length) AS io_size, target_desc, result
FROM pg_aios ORDER BY pid, io_id;

 pid | state     | operation | io_size    | target_desc                                                             | result
-----+-----------+-----------+------------+-------------------------------------------------------------------------+---------
  51 | SUBMITTED | readv     | 688 kB     | blocks 670470..670555 in file "pg_tblspc/16647/PG_18_202506291/5/16716" | UNKNOWN
  63 | SUBMITTED | readv     | 8192 bytes | block 1347556 in file "pg_tblspc/16647/PG_18_202506291/5/16719"         | UNKNOWN
  65 | SUBMITTED | readv     | 688 kB     | blocks 671236..671321 in file "pg_tblspc/16647/PG_18_202506291/5/16716" | UNKNOWN
  66 | SUBMITTED | readv     | 8192 bytes | block 1344674 in file "pg_tblspc/16647/PG_18_202506291/5/16719"         | UNKNOWN
  67 | SUBMITTED | readv     | 8192 bytes | block 1337819 in file "pg_tblspc/16647/PG_18_202506291/5/16719"         | UNKNOWN
  68 | SUBMITTED | readv     | 688 kB     | blocks 672002..672087 in file "pg_tblspc/16647/PG_18_202506291/5/16716" | UNKNOWN
  69 | SUBMITTED | readv     | 688 kB     | blocks 673964..674049 in file "pg_tblspc/16647/PG_18_202506291/5/16716" | UNKNOWN
pg_stat_io: Cumulative I/O stats
Die Katalogansicht pg_stat_io wurde in PostgreSQL 16 eingeführt, aber PostgreSQL 18 erweitert sie um Byte-Zähler (read_bytes, write_bytes, extend_bytes) und eine bessere Abdeckung von WAL- und Bulk-I/O-Kontexten. Die Timing-Spalten werden jedoch nur gefüllt, wenn wir die Timing-Parameter aktivieren – track_io_timing – Standard ist aus.
Eine praktische, clientseitige Ansicht der Beziehungs-I/O:
SELECT backend_type, context, sum(reads) AS reads, 
pg_size_pretty(sum(read_bytes)) AS read_bytes, 
round(sum(read_time)::numeric, 2) AS read_ms, sum(writes) AS writes, 
pg_size_pretty(sum(write_bytes)) AS write_bytes,
round(sum(write_time)::numeric, 2) AS write_ms, sum(extends) AS extends,
pg_size_pretty(sum(extend_bytes)) AS extend_bytes
FROM pg_stat_io
WHERE object = 'relation' AND backend_type IN ('client backend')
GROUP BY backend_type, context
ORDER BY backend_type, context;

 backend_type   | context   | reads   | read_bytes | read_ms   | writes | write_bytes | write_ms | extends | extend_bytes
----------------+-----------+---------+------------+-----------+--------+-------------+----------+---------+--------------
 client backend | bulkread  | 13833   | 9062 MB    | 124773.28 | 0      | 0 bytes     | 0.00     |         |
 client backend | bulkwrite | 0       | 0 bytes    | 0.00      | 0      | 0 bytes     | 0.00     | 0       | 0 bytes
 client backend | init      | 0       | 0 bytes    | 0.00      | 0      | 0 bytes     | 0.00     | 0       | 0 bytes
 client backend | normal    | 2265214 | 17 GB      | 553940.57 | 0      | 0 bytes     | 0.00     | 0       | 0 bytes
 client backend | vacuum    | 0       | 0 bytes    | 0.00      | 0      | 0 bytes     | 0.00     | 0       | 0 bytes


-- Top-Tabellen nach gelesenen Heap-Blöcken und Cache-Trefferrate

SELECT relid::regclass AS table_name, heap_blks_read, heap_blks_hit,
ROUND( CASE WHEN heap_blks_read + heap_blks_hit = 0 THEN 0
ELSE heap_blks_hit::numeric / (heap_blks_read + heap_blks_hit) * 100 END, 2) AS cache_hit_pct
FROM pg_statio_user_tables
ORDER BY heap_blks_read DESC LIMIT 20;

 table_name           | heap_blks_read | heap_blks_hit | cache_hit_pct
----------------------+----------------+---------------+---------------
 table1               | 18551282       | 3676632       | 16.54
 table2               | 1513673        | 102222970     | 98.54
 table3               | 19713          | 1034435       | 98.13
 ...


-- Top-Indizes nach gelesenen Indexblöcken und Cache-Trefferrate

SELECT relid::regclass AS table_name, indexrelid::regclass AS index_name,
idx_blks_read, idx_blks_hit 
FROM pg_statio_user_indexes
ORDER BY idx_blks_read DESC LIMIT 20;

 table_name | index_name      | idx_blks_read | idx_blks_hit
------------+-----------------+---------------+--------------
 table1     | idx_table1_date | 209289        | 141
 table2     | table2_pkey     | 37221         | 1223747
 table3     | table3_pkey     | 9825          | 3143947
...
Um eine Baseline vor/nach einem Testlauf zu erstellen, können wir die Statistiken zurücksetzen (als Superuser):

SELECT pg_stat_reset_shared(‚io‘);

Führen Sie dann unsere Arbeitslast aus und fragen Sie

pg_stat_io

erneut ab, um zu sehen, wie viele Bytes gelesen/geschrieben wurden und wie viel Zeit für das Warten auf E/A aufgewendet wurde.

Fazit

Das neue asynchrone I/O-Subsystem von PostgreSQL 18 ist ein bedeutender Schritt zur Verbesserung der I/O-Leistung für große Scans und Wartungsarbeiten. Durch die Überlappung von Lesevorgängen und die Möglichkeit, mehrere Anfragen gleichzeitig zu bearbeiten, können moderne Speichersysteme besser genutzt und die Abfragezeiten für datenintensive Workloads verkürzt werden. Mit den neuen Beobachtungsfunktionen in pg_aios und pg_stat_io können DBAs und Entwickler die AIO-Aktivität überwachen und Parameter optimieren, um die Leistung für ihre spezifischen Workloads zu optimieren. Da sich PostgreSQL ständig weiterentwickelt, können wir weitere Verbesserungen am AIO-Subsystem und eine breitere Abdeckung von Operationen erwarten, die von asynchronem I/O profitieren können.

PostgreSQL ist ein eingetragenes Warenzeichen der PostgreSQL Community Association of Canada.

In der Vergangenheit gab es viele Diskussionen über die Verwendung von UUID als Primärschlüssel in PostgreSQL. Für einige Anwendungen reicht selbst eine BIGINT-Spalte nicht aus: Es handelt sich um eine vorzeichenbehaftete 8-Byte-Ganzzahl mit einem Bereich von −9.223.372.036.854.775.808 bis +9.223.372.036.854.775.807. Obwohl diese Werte groß genug erscheinen, wird diese Zahl weniger beeindruckend, wenn wir an Webdienste denken, die täglich Milliarden oder mehr Datensätze sammeln. Einfache Ganzzahlwerte können auch zu Wertkonflikten in verteilten Systemen, in Data Lakehouses beim kombinieren von Daten aus mehreren Quelldatenbanken usw. führen.

Das Hauptproblem bei UUIDv4 als Primärschlüssel in PostgreSQL war jedoch nicht der fehlende Bereich, sondern die vollständige Zufälligkeit der Werte. Diese Zufälligkeit führt zu häufigen B-Baum-Seitenaufteilungen, einem stark fragmentierten Primärschlüsselindex und somit zu vielen zufälligen Festplatten-I/Os. Es gab bereits viele Artikel und Konferenzvorträge, die dieses Problem beschrieben haben. Was viele dieser Ressourcen jedoch nicht taten, war, tief in die On-Disk-Strukturen einzutauchen. Das wollte ich hier untersuchen.

Was sind UUIDs

UUID (Universally Unique Identifier) ist ein 16-Byte-Ganzzahlwert (128 Bit), der 2^128 mögliche Kombinationen aufweist (ungefähr 3,4 × 10^38). Dieser Bereich ist so groß, dass für die meisten Anwendungen die Wahrscheinlichkeit einer doppelten UUID praktisch null ist. Wikipedia zeigt eine Berechnung, die demonstriert, dass die Wahrscheinlichkeit, ein Duplikat innerhalb von 103 Billionen Version-4-UUIDs zu finden, etwa eins zu einer Milliarde beträgt. Eine weitere oft zitierte Faustregel besagt, dass man, um eine 50%ige Chance auf eine Kollision zu haben, etwa 1 Milliarde UUIDs pro Sekunde über etwa 86 Jahre generieren müsste.

Werte werden üblicherweise als 36-stelliger String mit Hexadezimalziffern und Bindestrichen dargestellt, zum Beispiel: f47ac10b-58cc-4372-a567-0e02b2c3d479. Das kanonische Layout ist 8-4-4-4-12 Zeichen. Das erste Zeichen im dritten Block und das erste Zeichen im vierten Block haben eine besondere Bedeutung: xxxxxxxx-xxxx-Vxxx-Wxxx-xxxxxxxxxxxxV kennzeichnet die UUID-Version (4 für UUIDv4, 7 für UUIDv7 usw.), W kodiert die Variante in ihren oberen 2 oder 3 Bits (die Layout-Familie der UUID).

Bis PostgreSQL 18 war die gängige Methode zur Generierung von UUIDs in PostgreSQL die Verwendung von Version 4 (zum Beispiel über gen_random_uuid() oder uuid_generate_v4() aus Erweiterungen). PostgreSQL 18 führt native Unterstützung für die neue zeitlich geordnete UUIDv7 über die Funktion uuidv7() ein und fügt auch uuidv4() als integrierten Alias für die ältere Funktion gen_random_uuid() hinzu. UUID Version 4 wird vollständig zufällig generiert (abgesehen von den festen Versions- und Variantenbits), sodass es keine inhärente Reihenfolge in den Werten gibt. UUID Version 7 generiert Werte, die zeitlich geordnet sind, da die ersten 48 Bits einen Big-Endian Unix-Epoch-Zeitstempel mit ungefähr Millisekunden-Granularität enthalten, gefolgt von zusätzlichen Sub-Millisekunden-Bits und Zufälligkeit.

Slonik, das PostgreSQL-Elefantenlogo

Testaufbau in PostgreSQL 18

Ich werde konkrete Ergebnisse anhand eines einfachen Testaufbaus zeigen – 2 verschiedene Tabellen mit der Spalte „id“, die einen generierten UUID-Wert (entweder v4 oder v7) enthält, der als Primärschlüssel verwendet wird, und der Spalte „ord“ mit sequenziell generiertem Bigint, wobei die Reihenfolge der Zeilenerstellung beibehalten wird.

-- UUIDv4 (completely random keys)
CREATE TABLE uuidv4_demo (
id uuid PRIMARY KEY DEFAULT uuidv4(), -- alias of gen_random_uuid()
ord bigint GENERATED ALWAYS AS IDENTITY
);

-- UUIDv7 (time-ordered keys)
CREATE TABLE uuidv7_demo (
id uuid PRIMARY KEY DEFAULT uuidv7(),
ord bigint GENERATED ALWAYS AS IDENTITY
);

-- 1M rows with UUIDv4
INSERT INTO uuidv4_demo (id) SELECT uuidv4() FROM generate_series(1, 1000000);

-- 1M rows with UUIDv7
INSERT INTO uuidv7_demo (id) SELECT uuidv7() FROM generate_series(1, 1000000);

VACUUM ANALYZE uuidv4_demo;
VACUUM ANALYZE uuidv7_demo;

Performance auf Abfrageebene: EXPLAIN ANALYZE

Als ersten Schritt vergleichen wir die Kosten der Sortierung nach UUID für die beiden Tabellen:

-- UUIDv4
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM uuidv4_demo ORDER BY id;

Index Scan using uuidv4_demo_pkey on uuidv4_demo (cost=0.42..60024.31 rows=1000000 width=24) (actual time=0.031..301.163 rows=1000000.00 loops=1)
Index Searches: 1
Buffers: shared hit=1004700 read=30
Planning Time: 0.109 ms
Execution Time: 318.005 ms

-- UUIDv7
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM uuidv7_demo ORDER BY id;

Index Scan using uuidv7_demo_pkey on uuidv7_demo (cost=0.42..36785.43 rows=1000000 width=24) (actual time=0.013..96.177 rows=1000000.00 loops=1)
Index Searches: 1
Buffers: shared hit=2821 read=7383
Planning Time: 0.040 ms
Execution Time: 113.305 ms

Die genauen Pufferzahlen hängen von Cache-Effekten ab, aber eines ist in diesem Durchlauf klar: Der Index-Scan über UUIDv7 benötigt etwa 100-mal weniger Puffer-Treffer und ist etwa dreimal schneller (113 ms vs. 318 ms) für dieselbe Million-Zeilen-ORDER BY id. Dies ist das erste Anzeichen dafür, dass UUIDv7 eine sehr praktikable Lösung für einen Primärschlüssel ist, wenn wir eine BIGINT-Spalte durch etwas ersetzen müssen, das einen viel größeren Speicherplatz und eine höhere Einzigartigkeit bietet, während es sich aus Sicht des Index immer noch wie ein sequenzieller Schlüssel verhält.

Einfügegeschwindigkeit – einfaches Benchmarking

Ursprünglich wollte ich ausgefeiltere Tests durchführen, aber selbst ein sehr einfacher, naiver Benchmark zeigte einen enormen Unterschied in der Einfügegeschwindigkeit. Ich verglich die Zeit, die benötigt wurde, um 50 Millionen Zeilen in eine leere Tabelle einzufügen, und dann noch einmal in eine Tabelle mit 50 Millionen vorhandenen Zeilen.

INSERT INTO uuidv4_demo (id) SELECT uuidv4() FROM generate_series(1, 50000000);
INSERT INTO uuidv7_demo (id) SELECT uuidv7() FROM generate_series(1, 50000000);

-- UUID v4 -- UUID v7
Empty table
Insert time: 1239839.702 ms (20:39.840) Insert time: 106343.314 ms (01:46.343)
Table size: 2489 MB Table size: 2489 MB
Index size: 1981 MB Index size: 1504 MB

Table with 50M rows
Insert time: 2776880.790 ms (46:16.881) Insert time: 100354.087 ms (01:40.354)
Table size: 4978 MB Table size: 4978 MB
Index size: 3956 MB Index size: 3008 MB

Wie wir sehen können, ist die Einfügegeschwindigkeit radikal unterschiedlich. Das Einfügen der ersten 50 Millionen Zeilen in eine leere Tabelle dauerte für UUIDv7 nur 1:46 Minuten, aber bereits 20 Minuten für UUIDv4. Die zweite Charge zeigte sogar einen 2-mal größeren Unterschied.

Wie Werte in der Tabelle verteilt sind

Diese Ergebnisse deuten auf enorme Unterschiede in den Indizes hin. Lassen Sie uns dies analysieren. Zuerst werden wir überprüfen, wie die Werte in der Tabelle verteilt sind. Ich verwende die folgende Abfrage für beide Tabellen (nur den Tabellennamen wechseln):

SELECT
row_number() OVER () AS seq_in_uuid_order,
id,
ord,
ctid
FROM uuidv4_demo
ORDER BY id
LIMIT 20;

Die Spalte seq_in_uuid_order ist lediglich die Zeilennummer in UUID-Reihenfolge, ord ist die Einfügereihenfolge, ctid zeigt den physischen Speicherort jedes Tupels im Heap an: (block_number, offset_in_block).

UUIDv4: random UUID order ⇒ random heap access

Wie sehen die Ergebnisse für UUIDv4 aus?

 seq_in_uuid_order | id                                   | ord    | ctid 
-------------------+--------------------------------------+--------+------------
1 | 00000abf-cc8e-4cb2-a91a-701a3c96bd36 | 673969 | (4292,125)
2 | 00001827-16fe-4aee-9bce-d30ca49ceb1d | 477118 | (3038,152)
3 | 00001a84-6d30-492f-866d-72c3b4e1edff | 815025 | (5191,38)
4 | 00002759-21d1-4889-9874-4a0099c75286 | 879671 | (5602,157)
5 | 00002b44-b1b5-473f-b63f-7554fa88018d | 729197 | (4644,89)
6 | 00002ceb-5332-44f4-a83b-fb8e9ba73599 | 797950 | (5082,76)
7 | 000040e2-f6ac-4b5e-870a-63ab04a5fa39 | 160314 | (1021,17)
8 | 000053d7-8450-4255-b320-fee8d6246c5b | 369644 | (2354,66)
9 | 00009c78-6eac-4210-baa9-45b835749838 | 463430 | (2951,123)
10 | 0000a118-f98e-4e4a-acb3-392006bcabb8 | 96325 | (613,84)
11 | 0000be99-344b-4529-aa4c-579104439b38 | 454804 | (2896,132)
12 | 00010300-fcc1-4ec4-ae16-110f93023068 | 52423 | (333,142)
13 | 00010c33-a4c9-4612-ba9a-6c5612fe44e6 | 82935 | (528,39)
14 | 00011fa2-32ce-4ee0-904a-13991d451934 | 988370 | (6295,55)
15 | 00012920-38c7-4371-bd15-72e2996af84d | 960556 | (6118,30)
16 | 00014240-7228-4998-87c1-e8b23b01194a | 66048 | (420,108)
17 | 00014423-15fc-42ca-89bd-1d0acf3e5ad2 | 250698 | (1596,126)
18 | 000160b9-a1d8-4ef0-8979-8640025c0406 | 106463 | (678,17)
19 | 0001711a-9656-4628-9d0c-1fb40620ba41 | 920459 | (5862,125)
20 | 000181d5-ee13-42c7-a9e7-0f2c52faeadb | 513817 | (3272,113)

Die Werte sind vollständig zufällig verteilt. Das Lesen von Zeilen in UUID-Reihenfolge ist hier praktisch sinnlos und führt direkt zu zufälligen Heap-Zugriffen bei Abfragen, die den Primärschlüsselindex verwenden.

UUIDv7: UUID-Reihenfolge folgt der Einfügereihenfolge

Andererseits werden UUIDv7-Werte in einer klaren Reihenfolge generiert:

 seq_in_uuid_order | id                                   | ord | ctid  
-------------------+--------------------------------------+-----+--------
1 | 019ad94d-0127-7aba-b9f6-18620afdea4a | 1 | (0,1)
2 | 019ad94d-0131-72b9-823e-89e41d1fad73 | 2 | (0,2)
3 | 019ad94d-0131-7384-b03d-8820be60f88e | 3 | (0,3)
4 | 019ad94d-0131-738b-b3c0-3f91a0b223a8 | 4 | (0,4)
5 | 019ad94d-0131-7391-ab84-a719ca98accf | 5 | (0,5)
6 | 019ad94d-0131-7396-b41d-7f9f27a179c4 | 6 | (0,6)
7 | 019ad94d-0131-739b-bdb3-4659aeaafbdd | 7 | (0,7)
8 | 019ad94d-0131-73a0-b271-7dba06512231 | 8 | (0,8)
9 | 019ad94d-0131-73a5-8911-5ec5d446c8a9 | 9 | (0,9)
10 | 019ad94d-0131-73aa-a4a3-0e5c14f09374 | 10 | (0,10)
11 | 019ad94d-0131-73af-ac4b-3710e221390e | 11 | (0,11)
12 | 019ad94d-0131-73b4-85d6-ed575d11e9cf | 12 | (0,12)
13 | 019ad94d-0131-73b9-b802-d5695f5bf781 | 13 | (0,13)
14 | 019ad94d-0131-73be-bcb0-b0775dab6dd4 | 14 | (0,14)
15 | 019ad94d-0131-73c3-9ec8-c7400b5c8983 | 15 | (0,15)
16 | 019ad94d-0131-73c8-b067-435258087b3a | 16 | (0,16)
17 | 019ad94d-0131-73cd-a03f-a28092604fb1 | 17 | (0,17)
18 | 019ad94d-0131-73d3-b4d5-02516d5667b5 | 18 | (0,18)
19 | 019ad94d-0131-73d8-9c41-86fa79f74673 | 19 | (0,19)
20 | 019ad94d-0131-73dd-b9f1-dcd07598c35d | 20 | (0,20)

Hier folgen seq_in_uuid_order, ord und ctid alle schön aufeinander – ord erhöht sich für jede Zeile um 1, ctid bewegt sich sequenziell durch die erste Heap-Seite, und die UUIDs selbst sind aufgrund des Zeitstempelpräfixes monoton. Für Index-Scans auf dem Primärschlüssel bedeutet dies, dass Postgres den Heap viel sequenzieller durchlaufen kann als mit UUIDv4.

Wie sequenziell sind diese Werte statistisch?

Nach VACUUM ANALYZE frage ich den Planer, was er über die Korrelation zwischen ID und der physischen Reihenfolge denkt:

SELECT
tablename,
attname,
correlation
FROM pg_stats
WHERE tablename IN ('uuidv4_demo', 'uuidv7_demo')
AND attname = 'id'
ORDER BY tablename, attname;

Ergebnis:

 tablename   | attname | correlation 
-------------+---------+---------------
uuidv4_demo | id | -0.0024808696
uuidv7_demo | id | 1

Die Statistiken bestätigen, was wir gerade gesehen haben:

Diese hohe Korrelation ist genau der Grund, warum UUIDv7 als Primärschlüssel für B-Baum-Indizes so attraktiv ist.

Primärschlüsselindizes: Größe, Leaf Pages, Dichte, Fragmentierung

Als Nächstes betrachte ich die Primärschlüsselindizes – ihre Größe, Anzahl der Leaf Pages, Dichte und Fragmentierung – mithilfe von pgstatindex:

SELECT 'uuidv4_demo_pkey' AS index_name, (pgstatindex('uuidv4_demo_pkey')).*;

index_name | uuidv4_demo_pkey
version | 4
tree_level | 2
index_size | 40026112
root_block_no. | 295
internal_pages | 24
leaf_pages. | 4861
empty_pages | 0
deleted_pages | 0
avg_leaf_density | 71
leaf_fragmentation | 49.99

SELECT 'uuidv7_demo_pkey' AS index_name, (pgstatindex('uuidv7_demo_pkey')).*;

index_name | uuidv7_demo_pkey
version | 4
tree_level | 2
index_size | 31563776
root_block_no | 295
internal_pages. | 20
leaf_pages | 3832
empty_pages | 0
deleted_pages | 0
avg_leaf_density | 89.98 -- i.e. standard 90% fillfactor
leaf_fragmentation | 0

Wir können sofort erkennen, dass der Primärschlüsselindex auf UUIDv4 etwa 26–27 % größer ist:

UUIDv4 zwingt den B-Baum also dazu, mehr Pages zu allozieren und diese weniger voll zu halten, und fragmentiert die Blattebene wesentlich stärker.

Tiefere Indexanalyse mit bt_multi_page_stats

Um tiefer einzusteigen, habe ich die B-Baum-Indizes Seite für Seite untersucht und einige Statistiken erstellt. Ich habe die folgende Abfrage für beide Indizes verwendet (nur den Indexnamen im CTE ändern). Die Abfrage berechnet die minimale, maximale und durchschnittliche Anzahl von Tupeln pro Index-Pages und überprüft auch, wie sequenziell Pages in der Indexdatei gespeichert sind:

WITH leaf AS (
 SELECT *
 FROM bt_multi_page_stats('uuidv4_demo_pkey', 1, -1) -- from block 1 to end
 WHERE type = 'l'
)
SELECT
 count(*) AS leaf_pages,
 min(blkno) AS first_leaf_blk,
 max(blkno) AS last_leaf_blk,
 max(blkno) - min(blkno) + 1 AS leaf_span,
 round( count(*)::numeric / (max(blkno) - min(blkno) + 1), 3) AS leaf_density_by_span,
 min(live_items) AS min_tuples_per_page,
 max(live_items) AS max_tuples_per_page,
 avg(live_items)::numeric(10,2) AS avg_tuples_per_page,
 sum(CASE WHEN btpo_next = blkno + 1 THEN 1 ELSE 0 END) AS contiguous_links,
 sum(CASE WHEN btpo_next  0 AND btpo_next  blkno + 1 THEN 1 ELSE 0 END) AS non_contiguous_links
FROM leaf;

Ergebnisse für UUIDv4:

-- uuidv4_demo_pkey
leaf_pages | 4861
first_leaf_blk | 1
last_leaf_blk | 4885
leaf_span | 4885
leaf_density_by_span | 0.995
min_tuples_per_page | 146
max_tuples_per_page | 291
avg_tuples_per_page | 206.72
contiguous_links | 0
non_contiguous_links | 4860

Ergebnisse für UUIDv7:

-- uuidv7_demo_pkey
leaf_pages | 3832
first_leaf_blk | 1
last_leaf_blk | 3852
leaf_span | 3852
leaf_density_by_span | 0.995
min_tuples_per_page | 109
max_tuples_per_page | 262
avg_tuples_per_page | 261.96
contiguous_links | 3812
non_contiguous_links | 19

Wie wir sehen können, hat der UUIDv4-Index mehr Pages, die sich über einen größeren Bereich von Blöcken verteilen, und obwohl er eine höhere minimale und maximale Anzahl von Tupeln pro Seite aufweist, ist seine durchschnittliche Anzahl von Tupeln pro Page (206,72) signifikant niedriger als für UUIDv7 (261,96).

Diese Zahlen können jedoch das Gesamtbild verschleiern. Schauen wir uns also Histogramme an, die die Anzahl der Tupel in Pages visualisieren. Dafür werde ich die folgende Abfrage mit Buckets zwischen 100 und 300 verwenden und nur nicht-leere Ergebnisse auflisten:

WITH leaf AS (
 SELECT live_items
 FROM bt_multi_page_stats('uuidv4_demo_pkey', 1, -1)
  WHERE type = 'l'
),
buckets AS (
 -- bucket lower bounds: 100, 110, ..., 290
 SELECT generate_series(100, 290, 10) AS bucket_min
)
SELECT
 b.bucket_min AS bucket_from,
 b.bucket_min + 9 AS bucket_to,
 COUNT(l.live_items) AS page_count
FROM buckets b
LEFT JOIN leaf l
 ON l.live_items BETWEEN b.bucket_min AND b.bucket_min + 9
GROUP BY b.bucket_min HAVING count(l.live_items) > 0
ORDER BY b.bucket_min;

Ergebnis für UUIDv4:

 bucket_from | bucket_to | page_count 
-------------+-----------+------------
140 | 149 | 159
150 | 159 | 435
160 | 169 | 388
170 | 179 | 390
180 | 189 | 427
190 | 199 | 466
200 | 209 | 430
210 | 219 | 387
220 | 229 | 416
230 | 239 | 293
240 | 249 | 296
250 | 259 | 228
260 | 269 | 214
270 | 279 | 171
280 | 289 | 140
290 | 299 | 21

Ergebnis für UUIDv7:

 bucket_from | bucket_to | page_count 
-------------+-----------+------------
100 | 109 | 1
260 | 269 | 3831

Diese Ergebnisse demonstrieren eindrucksvoll die enorme Fragmentierung des UUIDv4-Index und die stabile, kompakte Struktur des UUIDv7-Index. Die niedrigsten Buckets im UUIDv4-Histogramm zeigen Fälle von halb leeren Blattindexseiten (leaf index pages), andererseits überschreiten Seiten mit mehr als 270 Tupeln den 90 % Füllfaktor, da PostgreSQL den verbleibenden freien Speicherplatz nutzt, um Splits zu vermeiden. Im UUIDv7-Index sind alle Pages bis auf eine (die allerletzte im Baum) bis zum 90 % Standard-Füllfaktor gefüllt.

Ein weiteres wichtiges Ergebnis findet sich in den letzten beiden Spalten der Indexstatistiken:

btpo_next = blkno + 1 bedeutet, dass die nächste Page in der logischen B-Baum-Reihenfolge auch der nächste physische Block ist. Bei UUIDv4 geschieht dies in diesem Test nie – die Page sind vollständig fragmentiert und zufällig über die Indexstruktur verteilt. Bei UUIDv7 sind fast alle Pages zusammenhängend, d.h. sie folgen schön aufeinander.

Wenn wir den tatsächlichen Inhalt der Pages untersuchen, können wir sofort die Zufälligkeit von UUIDv4 im Vergleich zum sequenziellen Verhalten von UUIDv7 erkennen: UUIDv4-Pages verweisen auf Heap-Tupel, die über die gesamte Tabelle verstreut sind, während UUIDv7-Pages dazu neigen, auf enge Bereiche von Heap-Seiten zu verweisen. Das Ergebnis ist dasselbe Muster, das wir zuvor beim direkten Betrachten von ctid aus der Tabelle gesehen haben, daher werde ich die Roh-Dumps hier nicht wiederholen.

Ein kleiner Haken: eingebetteter Zeitstempel in UUIDv7

Es gibt einen kleinen Haken bei UUIDv7-Werten: Sie legen einen Zeitstempel der Erstellung offen. PostgreSQL 18 macht dies explizit über uuid_extract_timestamp():

SELECT 
id,
uuid_extract_timestamp(id) AS created_at_from_uuid
FROM uuidv7_demo
ORDER BY ord
LIMIT 5;

Beispielergebnisse:

 id                                   | created_at_from_uuid 
--------------------------------------+----------------------------
019ad94d-0127-7aba-b9f6-18620afdea4a | 2025-12-01 09:44:53.799+00
019ad94d-0131-72b9-823e-89e41d1fad73 | 2025-12-01 09:44:53.809+00
019ad94d-0131-7384-b03d-8820be60f88e | 2025-12-01 09:44:53.809+00
019ad94d-0131-738b-b3c0-3f91a0b223a8 | 2025-12-01 09:44:53.809+00
019ad94d-0131-7391-ab84-a719ca98accf | 2025-12-01 09:44:53.809+00

Betrachten wir die gesamte Wertefolge, können wir die Zeitdifferenzen zwischen den Datensatz-Erstellungen direkt aus den UUIDs analysieren, ohne eine separate Zeitstempelspalte. Für einige Anwendungen könnte dies als potenzielles Informationsleck angesehen werden (z. B. die Offenlegung ungefährer Erstellungszeiten oder Anfrageraten), während es vielen anderen höchstwahrscheinlich egal sein wird.

Zusammenfassung

Der Kompromiss besteht darin, dass UUIDv7 einen Zeitstempel einbettet, der ungefähre Erstellungszeiten offenlegen könnte, aber für die meisten Anwendungsfälle ist dies akzeptabel oder sogar nützlich. UUIDv7 verbessert also die Leistung und das physische Layout von UUID-Primärschlüsseln in PostgreSQL erheblich, nicht indem es die Zufälligkeit aufgibt, sondern indem es ein zeitlich geordnetes Präfix hinzufügt. In PostgreSQL 18 bietet uns das das Beste aus beiden Welten: den riesigen Bezeichnerraum und die Vorteile der verteilten Generierung von UUIDs, mit einem Indexverhalten, das einem klassischen sequenziellen BIGINT-Primärschlüssel viel näher kommt.


PostgreSQL ist eine Open-Source-Datenbank, die von den PostgreSQL-Entwicklern bereitgestellt wird. Das PostgreSQL-Elefantenlogo („Slonik“), Postgres und PostgreSQL sind eingetragene Marken der PostgreSQL Community Association.

Wir bei credativ bieten umfassende Support- und Beratungsleistungen für den Betrieb von PostgreSQL und anderen Open-Source-Systemen an.

Die European PostgreSQL Conference (PGConf.EU) ist eine der größten PostgreSQL-Veranstaltungen weltweit. Dieses Jahr fand sie vom 21. bis 24. Oktober in Riga, Lettland, statt. Unser Unternehmen, die credativ GmbH, war Bronzesponsor der Konferenz, und ich hatte das Privileg, credativ mit meinem Vortrag „Database in Distress: Testing and Repairing Different Types of Database Corruption“ zu vertreten. Zusätzlich war ich am Donnerstag und Freitag als Session-Host tätig. Die Konferenz selbst deckte ein breites Spektrum an PostgreSQL-Themen ab – von Cloud-nativen Bereitstellungen bis zur KI-Integration, von großen Migrationen bis zur Ausfallsicherheit. Nachfolgend finden Sie Höhepunkte der von mir besuchten Sessions, nach Tagen geordnet.

Mein Vortrag über Datenbankkorruption

Ich präsentierte meinen Vortrag am Freitagnachmittag. Darin tauchte ich in reale Fälle von PostgreSQL-Datenbankkorruption ein, denen ich in den letzten zwei Jahren begegnet bin. Um diese Probleme zu untersuchen, entwickelte ich ein Python-Tool, das Datenbankseiten absichtlich beschädigt, und untersuchte anschließend die Ergebnisse mithilfe der PostgreSQL-Erweiterung pageinspect. Während des Vortrags demonstrierte ich verschiedene Korruptionsszenarien und die von ihnen erzeugten Fehler und erklärte, wie jeder Fall zu diagnostizieren ist. Ein wichtiger Punkt war, dass PostgreSQL 18 Daten-Checksums standardmäßig bei initdb aktiviert. Checksums ermöglichen es, beschädigte Seiten während der Wiederherstellung zu erkennen und sicher „auf Null zu setzen“ (beschädigte Daten zu überspringen). Ohne Checksums können nur Seiten mit eindeutig beschädigten Headern automatisch mit der Einstellung zero_damaged_pages = on entfernt werden. Andere Arten von Korruption erfordern eine sorgfältige manuelle Wiederherstellung. Abschließend schlug ich Verbesserungen (im Code oder in den Einstellungen) vor, um die Wiederherstellung auf Clustern ohne Checksums zu erleichtern.

Dienstag: Kubernetes- und KI-Gipfel

Der Dienstag begann mit zwei halbtägigen Gipfeltreffen. Der PostgreSQL on Kubernetes Summit untersuchte den Betrieb von Postgres in Cloud-nativen Umgebungen. Die Referenten verglichen Kubernetes-Operatoren (CloudNativePG, Crunchy, Zalando usw.), Backup/Recovery in Kubernetes, Skalierungsstrategien, Monitoring und Zero-Downtime-Upgrades. Sie diskutierten Operator-Architekturen und Multi-Tenant-DBaaS-Anwendungsfälle. Die Teilnehmer erhielten praktische Einblicke in die Kompromisse verschiedener Operatoren und wie Kubernetes-basiertes Postgres für Hochverfügbarkeit betrieben werden kann.

Im PostgreSQL & AI Summit untersuchten Experten die Rolle von Postgres in KI-Anwendungen. Zu den Themen gehörten Vektorsuche (z. B. pgvector), hybride Suche, die Nutzung von Postgres als Kontextspeicher für KI-Agenten, konversationelle Abfrageschnittstellen und sogar die Optimierung von Postgres mit maschinellem Lernen. Die Referenten teilten Best Practices und Integrationsstrategien für den Aufbau KI-gesteuerter Lösungen mit Postgres. Kurz gesagt, der Gipfel untersuchte, wie PostgreSQL KI-Workloads dienen kann (und umgekehrt) und welche neuen Funktionen oder Erweiterungen für KI-Anwendungsfälle entstehen.

Mittwoch: Migrationen, Modellierung und Performance

Joaquim Oliveira (European Space Agency) erörterte die Verlagerung von Astronomie-Datensätzen (von den ESA-Missionen Gaia und Euclid) von Greenplum. Das Team erwog sowohl die Skalierung mit Citus als auch den Umzug in das neue Greenplum-basierte Cloud-Warehouse von EDB. Er behandelte die praktischen Vor- und Nachteile jedes Ansatzes sowie die betrieblichen Änderungen, die für die Neugestaltung solcher Exascale-Workloads erforderlich sind. Die wichtigste Erkenntnis war die Notwendigkeit, Architektur, Tools und administrative Änderungen zu planen, bevor eine Petabyte-Skala-Migration durchgeführt wird.

Boriss Mejias (EDB) betonte, dass die Datenmodellierung für Softwareprojekte von grundlegender Bedeutung ist. Anhand einer Schach-Turnier-Anwendung zeigte er, wie PostgreSQL die Datenintegrität durchsetzen kann. Durch die sorgfältige Auswahl von Datentypen und Constraints können Entwickler einen Großteil der Geschäftslogik direkt im Schema einbetten. Der Vortrag demonstrierte, wie man „PostgreSQL die Datenintegrität garantieren lässt“ und Anwendungslogik auf der Datenbankebene aufbaut.

Roberto Mello (Snowflake) beleuchtete die vielen Verbesserungen bei Optimierern und der Ausführung in Postgres 18. Zum Beispiel eliminiert der Planer nun automatisch unnötige Self-Joins, wandelt IN (VALUES…) Klauseln in effizientere Formen um und transformiert OR-Klauseln in Arrays für schnellere Index-Scans. Er beschleunigt auch Mengenoperationen (INTERSECT, EXCEPT), Window-Aggregates und optimiert SELECT DISTINCT und GROUP BY durch Neuanordnung von Schlüsseln und Ignorieren redundanter Spalten. Roberto verglich Query-Benchmarks über Postgres 16, 17 und 18 hinweg, um diese Fortschritte hervorzuheben.

Nelson Calero (Pythian) teilte einen „praktischen Leitfaden“ für die Migration von über 100 PostgreSQL-Datenbanken (von Gigabytes bis zu Multi-Terabytes) in die Cloud. Sein Team migrierte Hunderte von lokalen VM-Datenbanken zu Google Cloud SQL. Er erörterte Planung, Minimierung von Ausfallzeiten, Instanzdimensionierung, Tools und Post-Migrations-Tuning. Insbesondere wies er auf Herausforderungen wie die Handhabung von Upgrades älterer Versionen, Vererbungsschemata, PostGIS-Daten und Änderungen an Dienstkonten hin. Caleros Ratschläge umfassten die Auswahl der richtigen Cloud-Instanztypen, die Optimierung von Massendatenladungen und die Validierung der Performance nach der Migration.

Jan Wieremjewicz (Percona) berichtete über die Implementierung von Transparent Data Encryption (TDE) für Postgres über die pg_tde-Erweiterung. Er führte das Publikum durch die gesamte Reise – von der ersten Idee über Patch-Vorschläge bis hin zu Community-Feedback und Design-Kompromissen. Er erklärte, warum bestehende PostgreSQL-Hooks nicht ausreichten, welche Schwierigkeiten auftraten und wie Kundenfeedback das endgültige Design prägte. Dieser Vortrag diente als „Tagebuch“ darüber, was es braucht, um eine zentrale Verschlüsselungsfunktion durch den PostgreSQL-Entwicklungsprozess zu liefern.

Stefan Fercot (Data Egret) demonstrierte, wie Patroni (für Hochverfügbarkeit) zusammen mit pgBackRest (für Backups) verwendet wird. Er führte durch YAML-Konfigurationsbeispiele, die zeigten, wie pgBackRest in einen Patroni-verwalteten Cluster integriert wird. Stefan zeigte, wie Standby-Replikate aus pgBackRest-Backups wiederhergestellt und Point-in-Time Recovery (PITR) unter der Kontrolle von Patroni durchgeführt werden. Der Vortrag hob praktische operative Erkenntnisse hervor: Die Kombination dieser Tools bietet eine automatisierte, wiederholbare Notfallwiederherstellung für Postgres-Cluster.

Donnerstag: Cloud, EXPLAIN und Resilienz

Maximilian Stefanac und Philipp Thun (SAP SE) erklärten, wie SAP PostgreSQL innerhalb von Cloud Foundry (SAPs Open-Source-PaaS) einsetzt. Sie diskutierten Optimierungen und Skalierungsherausforderungen beim Betrieb von Postgres für die SAP Business Technology Platform. Im Laufe der Jahre hat das Cloud Foundry-Team von SAP Postgres auf AWS, Azure, Google Cloud und Alibaba Cloud bereitgestellt. Die Angebote der einzelnen Anbieter unterscheiden sich, daher ist die Vereinheitlichung von Automatisierung und Monitoring über Clouds hinweg eine große Herausforderung. Der Vortrag hob hervor, wie SAP Postgres-Performance-Verbesserungen an die Community zurückgibt und was es braucht, um große, Cloud-neutrale Postgres-Cluster zu betreiben.

In „EXPLAIN: Make It Make Sense“ half Aivars Kalvāns (Ebury) Entwicklern bei der Interpretation von Abfrageplänen. Er betonte, dass man nach der Identifizierung einer langsamen Abfrage verstehen muss, warum der Planer einen bestimmten Plan gewählt hat und ob dieser optimal ist. Aivars führte durch die EXPLAIN-Ausgabe und teilte Faustregeln zum Erkennen von Ineffizienzen – zum Beispiel das Aufspüren fehlender Indizes oder kostspieliger Operatoren. Er illustrierte gängige Abfrage-Anti-Patterns, die er in der Praxis gesehen hat, und zeigte, wie man sie datenbankfreundlicher umschreibt. Die Session gab praktische Tipps zum Dekodieren von EXPLAIN und zum Optimieren von Abfragen.

Chris Ellis (Nexteam) hob integrierte Postgres-Funktionen hervor, die die Anwendungsentwicklung vereinfachen. Anhand realer Anwendungsfälle – wie Ereignisplanung, Aufgabenwarteschlangen, Suche, Geolocation und die Handhabung heterogener Daten – zeigte er, wie Funktionen wie Bereichstypen, Volltextsuche und JSONB die Anwendungskomplexität reduzieren können. Für jeden Anwendungsfall demonstrierte Chris, welche Postgres-Funktion oder welcher Datentyp das Problem lösen könnte. Diese „Tipps & Tricks“-Tour bekräftigte, dass die Nutzung des reichhaltigen Funktionsumfangs von Postgres oft bedeutet, weniger benutzerdefinierten Code zu schreiben.

Andreas Geppert (Zürcher Kantonalbank) beschrieb ein Cross-Cloud-Replikations-Setup für Katastrophenresilienz. Angesichts der Anforderung, dass bei Ausfall eines Cloud-Anbieters höchstens 15 Minuten Daten verloren gehen dürfen, konnten sie keine physische Replikation verwenden (da ihre Cloud-Anbieter diese nicht unterstützen). Stattdessen bauten sie eine Multi-Cloud-Lösung unter Verwendung logischer Replikation auf. Der Vortrag behandelte, wie sie logische Replikate auch bei Schemaänderungen aktuell halten (wobei angemerkt wurde, dass die logische Replikation DDL nicht automatisch kopiert). Kurz gesagt, die logische Replikation ermöglichte einen resilienten Betrieb mit niedrigem RPO über Anbieter hinweg, trotz Schema-Evolution.

Derk van Veen (Adyen) beleuchtete die tiefere Logik hinter der Tabellenpartitionierung. Er betonte die Bedeutung, den richtigen Partitionsschlüssel zu finden – die „Leitfigur“ in Ihren Daten – und dann Partitionen über alle verwandten Tabellen hinweg auszurichten. Wenn Partitionen einen gemeinsamen Schlüssel und ausgerichtete Grenzen teilen, ergeben sich mehrere Vorteile: gute Performance, vereinfachte Wartung, integrierte Unterstützung für PII-Compliance, einfache Datenbereinigung und sogar transparentes Data Tiering. Derk warnte, dass schlecht geplante Partitionen die Performance erheblich beeinträchtigen können. In seinem Fall führte der Wechsel zu korrekt ausgerichteten Partitionen (und die Aktivierung von enable_partitionwise_join/_aggregate) zu einer 70-fachen Beschleunigung bei über 100 TB großen Finanztabellen. Alle von ihm vorgestellten Strategien wurden in Adyens mehreren Hundert TB großen Produktionsdatenbank erprobt.

Freitag: Weitere fortgeschrittene Themen

Nicholas Meyer (Academia.edu) stellte Thin Cloning vor, eine Technik, die Entwicklern echte Produktionsdaten-Snapshots zum Debuggen zur Verfügung stellt. Mit Tools wie DBLab Engine oder der Klonfunktion von Amazon Aurora erstellt Thin Cloning kostengünstig beschreibbare Kopien von Live-Daten. Dies ermöglicht es Entwicklern, Produktionsprobleme – einschließlich datenabhängiger Fehler – exakt zu reproduzieren, indem sie diese Klone realer Daten debuggen. Nicholas erklärte, wie Academia.edu Thin Clones verwendet, um subtile Fehler frühzeitig zu erkennen, indem Entwicklungs- und QA-Teams mit nahezu produktionsnahen Daten arbeiten.

Dave Pitts (Adyen) erklärte, warum zukünftige Postgres-Anwendungen sowohl B-Baum- als auch LSM-Baum- (log-strukturierte) Indizes verwenden könnten. Er skizzierte die grundlegenden Unterschiede: B-Bäume eignen sich hervorragend für Punktabfragen und ausgewogene Lese-/Schreibvorgänge, während LSM-Bäume einen hohen Schreibdurchsatz und Bereichs-Scans optimieren. Dave diskutierte „Fallstricke“ beim Wechsel von Workloads zwischen Indextypen. Der Vortrag verdeutlichte, wann welche Struktur vorteilhaft ist, und half Entwicklern und DBAs, den richtigen Index für ihre Workload zu wählen.

Ein von Jimmy Angelakos geleitetes Panel befasste sich mit dem Thema „How to Work with Other Postgres People“. Die Diskussion konzentrierte sich auf psychische Gesundheit, Burnout und Neurodiversität in der PostgreSQL-Community. Die Panelisten hoben hervor, dass ungelöste psychische Gesundheitsprobleme Stress und Fluktuation in Open-Source-Projekten verursachen. Sie teilten praktische Strategien für eine unterstützendere Kultur: persönliche „README“-Leitfäden zur Erklärung individueller Kommunikationspräferenzen, respektvolle und empathische Kommunikationspraktiken sowie konkrete Techniken zur Konfliktlösung. Ziel war es, die Postgres-Community einladender und resilienter zu gestalten, indem unterschiedliche Bedürfnisse verstanden und Mitwirkende effektiv unterstützt werden.

Lukas Fittl (pganalyze) präsentierte neue Tools zur Verfolgung von Änderungen an Abfrageplänen über die Zeit. Er zeigte, wie stabile Plan-IDs (analog zu Query-IDs) zugewiesen werden können, damit DBAs überwachen können, welche Abfragen welche Planformen verwenden. Lukas stellte die neue pg_stat_plans-Erweiterung (die die Funktionen von Postgres 18 nutzt) zur ressourcenschonenden Erfassung von Planstatistiken vor. Er erklärte, wie diese Erweiterung funktioniert und verglich sie mit älteren Tools (dem ursprünglichen pg_stat_plans, pg_store_plans usw.) und Cloud-Anbieter-Implementierungen. Dies erleichtert das Erkennen, wenn sich der Ausführungsplan einer Abfrage in der Produktion ändert, und unterstützt die Performance-Fehlerbehebung.

Ahsan Hadi (pgEdge) beschrieb pgEdge Enterprise PostgreSQL, eine zu 100 % Open-Source-verteilte Postgres-Plattform. pgEdge Enterprise Postgres bietet integrierte Hochverfügbarkeit (unter Verwendung von Patroni und Read Replicas) und die Möglichkeit, über globale Regionen hinweg zu skalieren. Ausgehend von einem Single-Node-Postgres können Benutzer zu einem Multi-Region-Cluster mit geo-verteilten Replikaten für extreme Verfügbarkeit und geringe Latenz wachsen. Ahsan demonstrierte, wie pgEdge für Organisationen konzipiert ist, die von einzelnen Instanzen auf große verteilte Bereitstellungen skalieren müssen, alles unter der Standard-Postgres-Lizenz.

Fazit

Die PGConf.EU 2025 war eine ausgezeichnete Veranstaltung zum Wissensaustausch und zum Lernen von der globalen PostgreSQL-Community. Ich war stolz, credativ zu vertreten und als Freiwilliger zu helfen, und ich bin dankbar für die vielen gewonnenen Erkenntnisse. Die oben genannten Sessions stellen nur eine Auswahl der reichhaltigen Inhalte dar, die auf der Konferenz behandelt wurden. Insgesamt machen die starke Community und die schnelle Innovation von PostgreSQL diese Konferenzen weiterhin sehr wertvoll. Ich freue mich darauf, das Gelernte in meiner Arbeit anzuwenden und zukünftige PGConf.EU-Veranstaltungen zu besuchen.

Wie ich in meinem Vortrag auf der PostgreSQL Conference Europe 2025 erläutert habe, kann Datenbeschädigung unbemerkt in jeder PostgreSQL-Datenbank vorhanden sein und bleibt unentdeckt, bis wir beschädigte Daten physisch lesen. Es kann viele Gründe geben, warum einige Datenblöcke in Tabellen oder anderen Objekten beschädigt werden können. Selbst moderne Speicherhardware ist alles andere als unfehlbar. Binäre Backups, die mit dem pg_basebackup-Tool erstellt wurden – einer sehr gängigen Backup-Strategie in der PostgreSQL-Umgebung – lassen diese Probleme verborgen. Denn sie prüfen keine Daten, sondern kopieren ganze Datendateien so, wie sie sind. Mit der Veröffentlichung von PostgreSQL 18 hat die Community beschlossen, Daten-Checksummen standardmäßig zu aktivieren – ein wichtiger Schritt zur frühzeitigen Erkennung dieser Fehler. Dieser Beitrag untersucht, wie PostgreSQL Checksummen implementiert, wie es Checksummenfehler behandelt und wie wir sie auf bestehenden Clustern aktivieren können.

Warum PostgreSQL-Checksummen wichtig sind

Warum Checksummen wichtig sind

Eine PostgreSQL-Tabelle oder ein Index wird in 8-KB-Seiten gespeichert. Wenn eine Seite auf die Festplatte geschrieben wird, berechnet PostgreSQL eine 16-Bit-Checksumme unter Verwendung jedes Bytes der Seite (mit Ausnahme des Checksummenfelds selbst) und der physischen Blockadresse der Seite. Die Checksumme wird im Seitenheader gespeichert. Bei jedem Lesevorgang berechnet PostgreSQL die Checksumme neu und vergleicht sie mit dem gespeicherten Wert. Da die Blockadresse Teil der Berechnung ist, erkennt das System sowohl Bit-Flips innerhalb der Seite als auch Seiten, die an den falschen Ort geschrieben wurden. Checksummen werden nicht verwaltet, während sich die Seite in gemeinsam genutzten Puffern befindet – sie werden nur berechnet, wenn die Seite aus dem Puffer-Cache in den Betriebssystem-Seiten-Cache geleert wird. Folglich kann eine falsche In-Memory-Seite erst erkannt werden, wenn sie geschrieben und wieder gelesen wird. PostgreSQL verwendet einen schnellen FNV-1a-Hash (mit CRC32C auf WAL-Datensätzen), der für die Leistung optimiert ist. Auf typischer Hardware scheinen die Kosten für die Berechnung der Checksumme gering zu sein. Eine Benchmarking-Studie ergab, dass die Strafe für normale Arbeitslasten in der Regel weniger als 2 % beträgt. In den Versionshinweisen zu PostgreSQL 18 wird bestätigt, dass der Overhead nicht Null ist, aber er wird zum Vorteil der Datenintegrität akzeptiert.

Änderungen in PostgreSQL 18
Version 18 aktiviert standardmäßig Daten-Checksummen. In früheren Versionen erforderte initdb das Flag –data-checksums. Die neuen Versionshinweise listen die Änderung explizit im Abschnitt Inkompatibilitäten auf: „Ändern Sie die initdb-Standardeinstellung, um Daten-Checksummen zu aktivieren… Checksummen können mit der neuen Option –no-data-checksums deaktiviert werden“.
Für DBAs hat diese Standardänderung zwei wichtige Konsequenzen:

Um zu sehen, ob unser Cluster Daten-Checksummen verwendet, überprüfen wir die schreibgeschützte Systemvariable data_checksums mit dem Befehl: „SHOW data_checksums;“ Ein Ergebnis von „ON“ bedeutet, dass Daten-Seiten-Checksummen aktiv sind.

Aktivieren und Deaktivieren von Checksummen mit pg_checksums
Checksummen sind eine Cluster-weite Eigenschaft und können nicht umgeschaltet werden, während der Server läuft. PostgreSQL liefert das pg_checksums-Dienstprogramm zum Überprüfen, Aktivieren oder Deaktivieren von Checksummen. Wichtige Punkte aus der Dokumentation:
Upgrade-Strategie
Wenn Sie einen Pre-18-Cluster ohne Checksummen aktualisieren, haben wir zwei Optionen:
Behandlung von Checksummenfehlern
Wenn PostgreSQL eine Checksummen-Nichtübereinstimmung erkennt, gibt es eine Warnung aus und löst einen Fehler aus. Zwei GUCs nur für Entwickler steuern, was als Nächstes geschieht. Sie sollten niemals im Normalbetrieb aktiviert werden, aber DBAs können sie zur Datenwiederherstellung verwenden:
Die folgenden vereinfachten Beispiele veranschaulichen diese Einstellungen:
-- Mit ignore_checksum_failure=off stoppt die Abfrage beim ersten Fehler:
test=# SELECT * FROM pg_toast.pg_toast_17453;
WARNING: page verification failed, calculated checksum 19601 but expected 152
ERROR: invalid page in block 0 of relation base/16384/16402

-- Mit ignore_checksum_failure=on protokolliert der Server Warnungen und scannt weiter, bis er gute Daten findet:
test=# SET ignore_checksum_failure = ON;
test=# SELECT * FROM pg_toast.pg_toast_17453;
WARNING: page verification failed, calculated checksum 29668 but expected 57724
WARNING: page verification failed, calculated checksum 63113 but expected 3172
WARNING: page verification failed, calculated checksum 59128 but expected 3155
Mit zero_damaged_pages=on werden ungültige Seiten genullt, anstatt einen Fehler zu verursachen. Die Abfrage wird fortgesetzt, aber die Daten auf diesen Seiten gehen verloren:
test=# SET zero_damaged_pages = ON;
test=# SELECT * FROM pg_toast.pg_toast_17453;
WARNING: page verification failed, calculated checksum 29668 but expected 57724
WARNING: invalid page in block 204 of relation base/16384/17464; zeroing out page
WARNING: page verification failed, calculated checksum 63113 but expected 3172
WARNING: invalid page in block 222 of relation base/16384/17464; zeroing out page

Intern führt der Puffer-Manager diese Nullung durch Aufrufen von memset() auf der 8-KB-Seite durch, wenn die Überprüfung fehlschlägt und das READ_BUFFERS_ZERO_ON_ERROR-Flag gesetzt ist. Wenn das Flag nicht gesetzt ist, wird der Puffer als ungültig markiert und ein Fehler wird ausgelöst. Wir müssen natürlich verstehen, dass Checksummen und ignore_checksum_failure- und zero_damaged_pages-Einstellungen beschädigte Datenblöcke nicht reparieren können. Diese Optionen sind letzte Auswege, um verbleibende Zeilen zu retten. Ihre Verwendung führt immer zu Datenverlusten. Sobald eine Seite im Speicher genullt ist, kann ihr vorheriger beschädigter Inhalt nicht wiederhergestellt werden, selbst wenn wir zero_damaged_pages wieder auf OFF setzen. Um ursprüngliche gute Daten zurückzubekommen, müssen wir sie aus einem bekannten guten Backup oder Standby wiederherstellen.

Autovacuum-Interaktion

Vacuum-Prozesse können beim Scannen von Tabellen auf beschädigte Seiten stoßen. Da das automatische Nullsetzen von Seiten Daten stillschweigend zerstören könnte, deaktiviert der Autovacuum-Launcher zero_damaged_pages für seine Worker zwangsweise. Der Quellcode ruft SetConfigOption mit „zero_damaged_pages“, „false“ mit einem Kommentar auf, der erklärt, dass diese gefährliche Option niemals nicht-interaktiv angewendet werden sollte. Auf diese Weise werden beschädigte Seiten nur dann genullt, wenn wir direkt mit ihnen arbeiten.

Warum wir Checksummen begrüßen sollten
Datenbeschädigung auf der Datenbank, die keine Checksummen verwendet, kann zu viel problematischeren Situationen führen. Ohne Checksummen können nur Seiten mit eindeutig beschädigtem Seitenheader erkannt und genullt werden. Unten sehen wir einen Test im PostgreSQL-Code, der zeigt, dass selbst diese Erkennung ohne Checksummen nicht einfach ist – siehe den Kommentar:
/*
* Die folgenden Überprüfungen beweisen nicht, dass der Header korrekt ist, sondern nur, dass
* es vernünftig genug aussieht, um in den Pufferpool aufgenommen zu werden. Spätere Verwendung von 
* der Block kann immer noch Probleme aufdecken, weshalb wir die
* Checksummenoption anbieten.
*/
if ((p->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
p->pd_lower <= p->pd_upper &&
p->pd_upper <= p->pd_special &&
p->pd_special <= BLCKSZ &&
p->pd_special == MAXALIGN(p->pd_special))
header_sane = true;

if (header_sane && !checksum_failure)
return true;
Im Allgemeinen testet dieser Code, ob wichtige Werte im Seitenheader in erwartete Beziehungen ihrer Werte passen. Eine gesunde Datenseite wird hier gezeigt:
SELECT * FROM page_header(get_raw_page('pg_toast.pg_toast_32840', 100));
 lsn | checksum | flags | lower | upper | special | pagesize | version | prune_xid
------------+----------+-------+-------+-------+---------+----------+---------+-----------
 0/2B2FCD68 | 0 | 4 | 40 | 64 | 8192 | 8192 | 4 | 0
(1 row)
So können nur Seitenheader mit eindeutig beschädigten Flag-Bits, lower, upper, special und/oder pagesize sicher als beschädigt erkannt werden. In diesem Fall erhalten wir eine Fehlermeldung:
ERROR: XX001-invalid page in block 578 of relation base/16384/28751
Und nur diese Seiten können genullt werden. Aber wenn der Header intakt ist (oder zumindest den obigen Test besteht), können wir viele verschiedene Fehler erhalten, die entweder durch beschädigte Item-IDs-Arrays oder beschädigte Systemspalten in Tupeln verursacht werden.
Beschädigte Item-IDs-Arrays enthalten falsche Offsets zum Anfang des Tupels und eine falsche Länge des Tupels. Diese beschädigten Zahlen können ungültige Speicheranforderungen oder sogar einen Absturz der Sitzung verursachen, die Daten liest:
ERROR: invalid memory alloc request size 18446744073709551594
DEBUG: server process (PID 76) was terminated by signal 11: Segmentation fault
Wenn die Item-IDs-Array-Werte intakt sind, aber Tupel beschädigt sind, sehen wir normalerweise verschiedene Fehler, die signalisieren, dass die Systemspalten xmin und xmax, die für die Überprüfung der Sichtbarkeit im Multiversion-Concurrency-Control-System entscheidend sind, nutzlose Werte enthalten:
58P01 - could not access status of transaction 3047172894
XX000 - MultiXactId 1074710815 has not been created yet -- apparent wraparound
WARNING: Concurrent insert in progress within table "test_table_bytea"

Bei diesen Fehlern können wir mit schwierigen und zeitaufwändigen manuellen Reparaturen und Datenrettungen konfrontiert werden, wenn wir kein zuverlässiges Backup haben, das wir zur Wiederherstellung von Daten verwenden könnten. Diese Beschreibungen zeigen deutlich, dass das Aktivieren von Daten-Checksummen eine sehr wichtige Änderung für die PostgreSQL-Community ist.

Fazit
Die Entscheidung von PostgreSQL 18, Daten-Seiten-Checksummen zu aktivieren, spiegelt die Erfahrung wider, dass die Auswirkungen auf die Leistung minimal und die Vorteile enorm sind. Checksummen erkennen eine Vielzahl von stillen Korruptionsereignissen, sodass wir Fälle, in denen Hardware schief geht, leichter diagnostizieren können. Sie machen auch die Rettung guter Daten viel schneller und einfacher – wenn aus irgendeinem Grund keine zuverlässigen Backups verfügbar sind.
Mehr dazu:
Wir helfen Ihnen gerne!

Ob Ansible, Debian, Proxmox, Kubernetes oder PostgreSQL, mit über 25 Jahren Entwicklungs- und Serviceerfahrung im open source-Bereich unterstützt Sie die credativ GmbH mit einem beispiellosen und individuell anpassbaren Support. Wir sind für Sie da, um Sie bei allen Ihren open source-Infrastrukturbedürfnissen zu unterstützen.

Haben Sie Fragen zu unserem Artikel oder möchten Sie, dass die Spezialisten von credativ einen Blick auf eine andere Software Ihrer Wahl werfen?
Dann schauen Sie vorbei und nehmen Sie über unser Kontaktformular Kontakt auf oder schreiben Sie uns eine E-Mail an info@credativ.de.

Über credativ

Die credativ GmbH ist ein herstellerunabhängiges Beratungs- und Dienstleistungsunternehmen mit Sitz in Mönchengladbach.

 

PG Day Austria 2025 in ViennaAm 04.09.2025 fand im Apothekertrakt des Schlosses Schönbrunn in Wien nach den vorherigen Events 2021 und 2022 der dritte pgday Austria statt.
153 Teilnehmer hatten die Möglichkeit, insgesamt 21 Talks und 15 verschiedene Sponsoren zu besuchen und sich über alle möglichen Themen rund um PostgreSQL und der Community zu unterhalten.

Diese mal ebenfalls vertreten war der Sheldrick Wildlife Trust der sich er Rettung von Elefanten und Nashörnern widmet. Es konnte sich über das Projekt informiert werden, Spenden abgegeben und an einem Gewinnspiel teilgenommen werden.

Die Talks reichten dabei von den Themen Benchmarking über Crash-Recovery bis hin zu Big-Data.
Auch unser Kollege war mit seinem Talk „Postgres with many data: To MAXINT and beyond“ vertreten.

Als besonderes Highlight gab es am Ende des Tages vor dem Networking-Event noch neben den fast schon obligatorischen Lightning-Talks ein „Celebrity DB Deathmatch“ bei dem sich diverse Vertreter der Community für ein sehr unterhaltsames Bühnenstück zusammenfanden und in verschiedenen Disziplinen die beste Datenbank zu finden. Zur (zugegebenermaßen nicht großen) Überraschung aller hat PostgreSQL tatsächlich in jeder Kategorie brillieren können.

Wir hatten zusätzlich die Möglichkeit durch unsere Präsenz mit eigenem Stand eine Menge sehr interessante Gespräche und auch Diskussionen mit verschiedenen Mitgliedern der Community sowie auch den Sponsoren und generell den Besuchern zu führen.
Erstmalig war auch der neue Geschäftsführer der credativ GmbH nach unserer Wieder-Eigenständigkeit vor Ort und hat sich selbst ein Bild verschafft.

Alles in allem war es eine (noch) etwas kleinere, aber nichtsdestotrotz wie immer sehr lehrreiche und familiäre Veranstaltung und wir freuen uns bereits auf die Nächste und bedanken uns bei den Veranstaltern und auch dem gesamten Team vor Ort und im Hintergrund.

Dieses Wochenende war es wieder soweit, die FrOScon 2025 fand statt. Bei perfektem Sommerwetter - nicht zu warm, nicht zu kalt - fand diese wie jedes Jahr an der Hochschule in Sank Augustin statt. Und doch war dieses Jahr anders, 20 Jahre FrOScon wurden gefeiert. Mit der Konferenz bin ich besonders verbunden, nicht nur ist diese Konferenz die größte, lokale OpenSource Konferenz, ich bin auch seit vielen Jahren ein Teil von ihr. Unzählige Vorträge habe ich gehalten, Debian Stände organisiert, Entwickertracks bespielt und organisiert. 2007 hatte ich sogar das Vergnügen Teil der Orga zu sein. FrOScon bedeutet in gewisser Weise ein Heimkommen. An jeder Ecke sieht man vertraute Gesichter, die man seit vielen Jahren kennt, ehemalige und aktuelle Kollegen, gute Freunde aus der OpenSource Szene. Vergleichbares gibt es so nur auf der Fosdem in Brüssel - nicht verwunderlich, stellt doch die Fosdem das große Vorbild für die FrOScon dar.

Eine Zeitreise - 20 Jahre FrOScon

Bereits bei der ersten Ausgabe der FrOScon war ich als Aussteller für das Debian Projekt, Vortrager und Organisator des Debian Tracks (zusammen mit meinem - immer noch - Kollegen Martin Zobel-Helas) vertreten. Den Vortrag Techniken zur Spambekämpfung könnte ich vermutlich heute immer noch 1:1 halten, das Thema hat jedenfalls nichts an Brisanz verloren. Die erste FrOScon konnte mit sehr achtbaren ca. 300 Besuchern ihre Tore schliessen. Ich war so begeistert das ich im Folgejahr direkt an der Organisation teilgenommen habe - dies war auch das Jahr wo wir die berühmte Hüpfburg einführen konnten ;). Im Verlaufe der 20 Jahre hat sich die FrOScon zu einem der größten deutschen OSS Communityevents entwicklelt und kann heute etwa 1500 Besucher jedes Jahr verzeichnen. In den Jahren konnte die Konferenz auch diverse prominente Keynote Speaker anziehen, stellvertretend seien hier nur Andrew Tanenbaum und Richard Stallman zu erwähnen. Damals wie heute kann ich sagen das die FrOScon die beste Konferenz im Westen ist.

FrOScon 2025


Kommen wir zum eigentlichen Inhalt des Blogeintrages - der FrOScon 2025. Wir waren dieses Jahr mit 2 Vorträgen und einigen Kollegen als Besucher vertreten. Mein Kollege Patrick Lauer hat sich einem Saal des Thema "Postgres with many data" gewidmet. Dank des c3voc ist der Vortrag auch für alle die nicht dabei gewesen sind als Stream verfügbar, so kann jeder diese Wissenslücke auch nachträglich füllen.

Auch ich war in diesem Jahr wieder mit einem Vortrag vertreten. Das Thema Proxmox VE und externe Blockdevices stand auf der Agenda. Hier habe ich über die verschiedenen Blockdevicetypen wie ISCSI, Fibrechannel oder NVMEoF und wie man mit ihnen umgehen kann referiert. Auch dieser Vortrag steht im Videoarchiv des c3voc bereit.

Fazit

Auch diese FrOScon stellte wieder ein gelungenes Event für Communitymitglieder und interessiertes Fachpersonal da. Ein bisschen ist es wie ein Klassentreffen, man trifft lauter bekannte Gesichter und ist gespannt was sich bei den Leuten in der Zeit seit dem letzten Treffen getan hat. Auch wenn ich dann schon hoffentlich in Rente sein sollte würde ich mich sehr freuen auch auf der FrOScon #40 einen Vortrag halten zu können ;).

PostgreSQL trifft auf OAuth2Mit der Veröffentlichung der Beta-Version von PostgreSQL 18 wurde eine spannende neue Funktion eingeführt: native Unterstützung für OAuth2. Da ich im Netz weder eine Anleitung noch einen Blogbeitrag gefunden habe, der zeigt, wie man diese Funktion nutzt, habe ich aus Neugier beschlossen, es selbst auszuprobieren.

Ich habe ein neues Projektverzeichnis angelegt und mit dem Experimentieren begonnen.

Umgebung und Zielsetzung

Ziel war es, PostgreSQL 18 Beta mit OAuth2-Authentifizierung einzurichten, für den Identity Provider habe ich Keycloak gewählt.

Ich habe mich dazu entschieden Docker Compose zu verwenden, um beide Dienste schnell und unkompliziert hoch fahren zu können.

Docker-Compose-Konfiguration

Hier ist das docker-compose.yml, das ich verwendet habe:

services:
  mock-oauth2:
    image: quay.io/keycloak/keycloak:24.0
    command: start --https-port=8080 --https-key-store-file=/etc/x509/https/localhost.p12 --https-key-store-password=changeit --import-realm --hostname-strict=false --hostname-url=https://mock-oauth2:8080
    ports:
      - "8080:8080"
    environment:
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
    volumes:
      - ./certs:/etc/x509/https
      - ./keycloak-realm.json:/opt/keycloak/data/import/realm.json
    networks:
      - pgnet

  postgres18:
    build: .
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: postgres
    volumes:
      - ./postgres/postgresql.conf:/etc/postgresql/postgresql.conf
      - ./postgres/pg_hba.conf:/etc/postgresql/pg_hba.conf
    command: ["-c", "config_file=/etc/postgresql/postgresql.conf"]
    networks:
      - pgnet

networks:
  pgnet:
    driver: bridge

PostgreSQL erwartet eine Verbindung zum OAuth2-Issuer über HTTPS. Dieselbe URL muss auch über den Host erreichbar sein, etwa bei der Nutzung des Keycloak-UI. Das bedeutet: Das Zertifikat muss sowohl auf dem Host als auch im Container gültig und vertrauenswürdig sein.

Der verwendete Hostname (z. B. https://mock-oauth2:8080) muss in beiden Umgebungen korrekt auflösbar sein.

Dazu habe ich folgende Zeile in meiner /etc/hosts-Datei auf dem Host ergänzt:

127.0.0.1 mock-oauth2

So konnten sowohl der PostgreSQL-Container als auch mein Host den Keycloak-Service unter https://mock-oauth2:8080 erreichen.

TLS-Zertifikat für Keycloak

Keycloak benötigt zwingend einen HTTPS-Endpunkt, damit die OAuth2-Issuer-URL von PostgreSQL akzeptiert wird.

Dafür habe ich ein selbstsigniertes Zertifikat erstellt und es in ein .p12-Keystore-Paket umgewandelt, das Keycloak verwenden kann.

Das .p12-Zertifikat wird über folgenden Mount in den Container eingebunden:

volumes:
  - ./certs:/etc/x509/https

Im certs-Verzeichnis liegt die Datei localhost.p12, welche ich aus meinem selbstsignierten Schlüssel und Zertifikat folgendermaßen erstellt habe:

openssl req -x509 -nodes -days 365 \
  -newkey rsa:2048 \
  -keyout server.key \
  -out server.crt \
  -subj "/CN=mock-oauth2" \
  -addext "subjectAltName = DNS:mock-oauth2,DNS:localhost,IP:127.0.0.1"

Keycloak-Realm-Konfiguration

Ich habe eine minimalistische Realm-Datei für Keycloak erstellt. Sie enthält einen Client namens postgres und einen Benutzer mit entsprechenden Anmeldedaten.

Inhalt von keycloak-realm.json:

{
  "realm": "pg",
  "enabled": true,
  "clients": [
    {
      "clientId": "postgres",
      "enabled": true,
      "publicClient": false,
      "redirectUris": ["*"],
      "protocol": "openid-connect",
      "secret": "postgres",
      "directAccessGrantsEnabled": true,
      "standardFlowEnabled": true
    }
  ],
  "users": [
    {
      "username": "postgres",
      "enabled": true,
      "credentials": [
        {
          "type": "password",
          "value": "postgres"
        }
      ]
    }
  ]
}

Nach dem Import des Realms war Keycloak bereit und der Standard-Scope im UI sichtbar.

Installation von libpq-oauth und oauth_validator

Die offizielle PostgreSQL-Image musste ich erweitern, um zusätzliche Abhängigkeiten wie die Erweiterung libpq-oauth sowie den Validator oauth_validator zu installieren.

PostgreSQL 18 unterstützt OAuth2-Authentifizierung experimentell. Allerdings liefert PostgreSQL keine eigene Validator-Bibliothek mit. In der offiziellen Dokumentation heißt es:

Die PostgreSQL-Distribution enthält keine Bibliotheken zur Validierung von OAuth2-Token. Benutzer müssen eine eigene Lösung bereitstellen oder selbst kompilieren.
PostgreSQL
Docs – oauth_validator_libraries

Für Tests habe ich folgende Open-Source-Implementierung verwendet:

github.com/TantorLabs/oauth_validator

Diese minimalistische C-Bibliothek kann kompiliert und als oauth_validator_library in PostgreSQL verwendet werden.

Verwendetes Dockerfile

FROM postgres:18beta1

USER root

RUN apt-get update \
    && apt-get install -y libpq-oauth build-essential libkrb5-dev \
       libsasl2-dev libcurl4-openssl-dev postgresql-server-dev-18 git \
    && git clone https://github.com/TantorLabs/oauth_validator.git /tmp/oauth_validator \
    && cd /tmp/oauth_validator \
    && make && make install \
    && rm -rf /tmp/oauth_validator \
    && apt-get remove -y build-essential git \
    && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*

Dieses Image habe ich dann für den `postgres18`-Service in meinem Docker-Compose-Setup verwendet.

PostgreSQL mit der Keycloak-CA vertrauen lassen

PostgreSQL muss dem von Keycloak präsentierten Zertifikat vertrauen, sonst wird die Verbindung zum OAuth2-Issuer abgelehnt.

Dazu habe ich die Datei `mock-oauth.crt` in den PostgreSQL-Container kopiert und im typischen CA-Pfad abgelegt:

/usr/local/share/ca-certificates/

Dann habe ich innerhalb des Containers folgendes Kommando ausgeführt:

update-ca-certificates

Danach wurde das Zertifikat akzeptiert und PostgreSQL konnte die Verbindung zum HTTPS-Issuer erfolgreich überprüfen.

PostgreSQL-Konfiguration

In pg_hba.conf habe ich folgende Zeile ergänzt:

host all all all oauth scope="profile" issuer="https://mock-oauth2:8080/realms/pg" map="oauthmap"

In pg_ident.conf habe ich die vom Token bereitgestellte Identität dem PostgreSQL-Benutzer zugeordnet:

oauthmap "postgresID" "postgres"

Diese Zuordnung muss ggf. angepasst werden – abhängig davon, wie dein Keycloak-Client konfiguriert ist und welches Feld (z. B.
preferred_username oder sub) im Token übergeben wird.

Verbindungstest mit OAuth2

Zum Testen der Verbindung habe ich folgenden `psql`-Befehl verwendet:

psql "host=localhost \
      port=5432 \
      dbname=postgres \
      user=postgres \
      oauth_issuer=https://mock-oauth2:8080/realms/pg \
      oauth_client_id=postgres \
      oauth_client_secret=changeme \
      oauth_scope=profile"

Nach dem Aufruf erscheint eine Device-Code-Meldung wie:

Besuchen Sie `https://mock-oauth2:8080/realms/pg/device` und geben Sie den Code FBAD-XXYZ ein.

Nach Anmeldung mit den Benutzeranmeldedaten stellt `psql` erfolgreich eine Verbindung zu PostgreSQL über OAuth2 her.

Erkenntnisse und Tipps

Fazit

Das war ein spannendes, praxisnahes Experiment mit einer vielversprechenden neuen Funktion in PostgreSQL. OAuth2-Integration bringt PostgreSQL näher an moderne Identity-Management-Lösungen heran und vereinfacht den Betrieb in Umgebungen mit zentralisierter Authentifizierung.

Am Donnerstag, 26. Juni, und Freitag, 27. Juni 2025, hatten mein Kollege Patrick Lauer und ich die großartige Gelegenheit, am Swiss PGDay 2025 teilzunehmen, der an der OST Ostschweizer Fachhochschule in Rapperswil stattfand. Diese zweitägige PostgreSQL-Konferenz bot zwei parallele Vortragsreihen in englischer und deutscher Sprache und brachte vor allem Anwender und Experten aus der ganzen Schweiz zusammen. Unser Unternehmen credativ gehörte zu den Unterstützern der diesjährigen Konferenz.

Während der Veranstaltung hielt Patrick einen spannenden Vortrag mit dem Titel „Postgres mit vielen Daten: Bis MAXINT und darüber hinaus“, der auf früheren Diskussionen über den Einsatz von Postgres in großem Maßstab aufbaute. Er hob die praktischen Probleme hervor, die bei der Verarbeitung extrem großer Datensätze in PostgreSQL auftreten – beispielsweise, wie selbst ein einfacher SELECT COUNT(*) schmerzhaft langsam werden kann und wie Backups und Wiederherstellungen bei sehr großen Datensätzen Tage dauern können. Außerdem stellte er Strategien für ein effektives Leistungsmanagement in diesen Größenordnungen vor.

Ich präsentierte eine deutlich überarbeitete Version meines Vortrags „Aufbau eines Data Lakehouse mit PostgreSQL: Eintauchen in Formate, Tools, Techniken und Strategien”. Er behandelte moderne Datenformate und Frameworks wie Apache Iceberg und ging auf die wichtigsten Herausforderungen in Lakehouse-Architekturen ein – von Governance, Datenschutz und Compliance bis hin zu Datenqualitätsprüfungen und Anwendungsfällen für KI/ML. Der Vortrag hob die Fähigkeit von PostgreSQL hervor, eine zentrale Rolle in der heutigen Data-Lakehouse- und KI-Landschaft zu spielen. Zum Abschluss der Konferenz hielt ich einen kurzen Blitzvortrag, in dem ich unser neues Open-Source-Migrationstool „credativ-pg-migrator” vorstellte.

 
(c) Fotos von Gülçin Yıldırım Jelinek

Das Konferenzprogramm war vollgepackt mit vielen hochwertigen und aufschlussreichen Vorträgen. Besonders hervorheben möchten wir:

* Bruce Momjian – „Wie Open Source und Demokratie Postgres vorantreiben”: In seiner Keynote skizzierte Bruce Momjian, wie das Open-Source-Entwicklungsmodell und die demokratische Governance von PostgreSQL zum Erfolg der Datenbank beigetragen haben. Er erläuterte die Unterschiede zwischen Open-Source- und proprietären Modellen, gab einen Überblick über die Geschichte der Governance von PostgreSQL und zeigte auf, wie demokratische, offene Prozesse zu robuster Software und einer vielversprechenden Zukunft für Postgres führen.

* Gülçin Yıldırım Jelinek – „Anatomie von Sperren auf Tabellenebene in PostgreSQL”: In dieser Sitzung wurden die Grundlagen der Sperrmechanismen auf Tabellenebene in PostgreSQL behandelt. Es wurde erklärt, wie verschiedene Sperrmodi während Schemaänderungen erworben und in eine Warteschlange gestellt werden, damit die Teilnehmer verstehen, wie sie Sperrkonflikte verwalten, Ausfallzeiten minimieren und Deadlocks bei DDL-Operationen mit hoher Parallelität vermeiden können.

* Aarno Aukia – „Betrieb von PostgreSQL in großem Maßstab: Erkenntnisse aus Hunderten von Instanzen in regulierten privaten Clouds“: Der Referent berichtete über seine Erfahrungen mit dem Betrieb umfangreicher Postgres-Umgebungen in stark regulierten Branchen. Er diskutierte Architekturmuster, Automatisierungsstrategien und „Day-2-Operations“-Praktiken, die VSHN einsetzt, um strenge Anforderungen an Verfügbarkeit, Compliance und Audits zu erfüllen, darunter sichere Mandantenfähigkeit, deklarative Bereitstellungen, Backups, Überwachung und Lebenszyklusmanagement in unternehmenskritischen Cloud-nativen Setups.

* Bertrand Hartwig-Peillon – „pgAssistant”: Der Autor stellte pgAssistant vor, ein Open-Source-Tool, das Entwicklern dabei hilft, PostgreSQL-Schemas und -Abfragen vor der Produktionsbereitstellung zu optimieren. Er demonstrierte, wie pgAssistant deterministische Analysen mit einem KI-gesteuerten Ansatz kombiniert, um Schema-Inkonsistenzen zu erkennen und Optimierungen vorzuschlagen, wodurch Best Practices und Leistungsoptimierungen innerhalb von Entwicklungsworkflows effektiv automatisiert werden.

* Gianni Ciolli – „The Why and What of WAL”: Gianni Ciolli gab in seiner gewohnt prägnanten italienischen Art einen Überblick über die Geschichte und die Funktionsweise des Write-Ahead Log (WAL) von PostgreSQL. Er erläuterte die zentrale Rolle von WAL in PostgreSQL für Absturzsicherheit, Backups und Replikation und zeigte Beispiele für WAL-fähige Funktionen wie schnelle Absturzbehebung, effiziente Hot-Backups, physische Replikation und logische Dekodierung.

* Daniel Krefl – „Hacking pgvector for performance”: Der Referent stellte eine verbesserte Version der pgvector-Erweiterung für die Verarbeitung großer Datenmengen vor, die durch die Verwaltung des Vektorindex außerhalb des PostgreSQL-Speichers und die Auslagerung von Berechnungen, einschließlich GPU-Integration, optimiert wurde. Er beschrieb detailliert den Prozess der Auslagerung der Kernlogik von pgvector zur Verbesserung der Geschwindigkeit und demonstrierte bemerkenswerte Leistungssteigerungen im Rahmen des EU-AERO-Projekts. Er sprach auch über die verteilten PostgreSQL-Versionen XC, XL und TBase, die leider noch auf der alten Version 10 basieren, und darüber, wie er Änderungen aus diesen Projekten in die Version 16 portiert hat.

* Luigi Nardi – „Eine Benchmark-Studie zu den Auswirkungen der Optimierung von PostgreSQL-Serverparametern”: Luigi Nardi präsentierte umfassende Benchmark-Ergebnisse zur Optimierung von PostgreSQL-Konfigurationsparametern. Er hob hervor, dass viele Benutzer die Standardeinstellungen verwenden, und zeigte, wie durch eine richtige Optimierung für verschiedene Workloads (OLTP, OLAP usw.) erhebliche Leistungsverbesserungen erzielt werden können. Dabei lieferte er umsetzbare Erkenntnisse, die auf bestimmte Umgebungen zugeschnitten sind.

* Renzo Dani – „Von Oracle zu PostgreSQL: Eine SCHWIERIGE Reise und ein Open-Source-Erwachen”: Der Autor berichtete über seine Erfahrungen bei der Migration einer komplexen Unternehmensanwendung von Oracle zu PostgreSQL und ging dabei auf bedeutende Herausforderungen wie implizite Typumwandlungen, Unterschiede bei der Funktionsüberladung, Probleme mit JDBC-Treibern und SQL-Validierungsprobleme ein. Er hob auch die Vorteile hervor, darunter schnellere CI-Pipelines, flexiblere Bereitstellungen und Innovationsmöglichkeiten durch das Open-Source-Produkt Postgres, und gab praktische Ratschläge zu Migrationstools, Teststrategien und zum Management von Kompromissen.


(c) Foto: Swiss PostgreSQL User Group

Am Ende des ersten Tages genossen alle Teilnehmer ein Networking-Dinner. Wir möchten uns beide ganz herzlich bei den Organisatoren des Swiss PGDay (Swiss PostgreSQL User Group) für eine großartige Veranstaltung bedanken. Der Swiss PGDay 2025 war eine unvergessliche und wertvolle Erfahrung, die großartige Lern- und Networking-Möglichkeiten bot. Wir sind auch credativ sehr dankbar, dass sie uns die Teilnahme ermöglicht haben, und freuen uns schon auf die nächsten Ausgaben dieser hervorragenden Konferenz.

Viele Unternehmen denken heutzutage darüber nach, ihre Datenbanken von Legacy- oder proprietären Systemen auf PostgreSQL zu migrieren. Das primäre Ziel ist es, die Kosten zu senken, die Fähigkeiten zu verbessern und die langfristige Nachhaltigkeit zu gewährleisten. Doch allein die Vorstellung, auf PostgreSQL zu migrieren, kann schon überwältigend sein. Sehr oft ist das Wissen über die Altanwendungen begrenzt oder sogar verloren gegangen. In einigen Fällen nimmt die Unterstützung durch den Hersteller ab, und der Expertenpool und die Unterstützung durch die Community schrumpfen. Außerdem laufen alte Datenbanken oft auf veralteter Hardware und alten Betriebssystemen, was weitere Risiken und Einschränkungen mit sich bringt. (mehr …)

Wir freuen uns sehr, Gastgeber des 5. PostgreSQL User Group NRW Meetups zu sein – und das in unseren neuen Geschäftsräumen direkt am Borussia-Park in Mönchengladbach! (mehr …)

Die PGConf.DE 2025, die 9. jährliche PostgreSQL-Konferenz in Deutschland, fand vom 8. bis 9. Mai 2025 im Marriott Hotel in der Nähe des Potsdamer Platzes in Berlin statt. Die Veranstaltung brachte zwei Tage lang PostgreSQL-Enthusiasten, Entwickler, DBAs und Industriesponsoren zu faszinierenden Vorträgen in vier parallelen Tracks zusammen. Mit 347 Teilnehmern war es die bisher größte Veranstaltung. Die gesamte Konferenz war sehr gut organisiert, und daher gebührt allen Organisatoren – insbesondere Andreas Scherbaum, dem Hauptorganisator – ein besonderer Dank für ihre Bemühungen und ihre harte Arbeit.

Unser Unternehmen, die credativ GmbH, ist wieder unabhängig und nahm als Gold-Sponsor teil. Der CTO von credativ, Alexander Wirt, der Head of Sales & Marketing, Peter Dreuw, und der Teamleiter des Datenbank-Teams, Dr. Tobias Kauder, standen den Teilnehmern am Stand von credativ zur Verfügung. Vielen Dank an unseren Teamkollegen Sascha Spettmann für die Lieferung aller Utensilien und Werbetafeln zur Konferenz und zurück.

   

Insgesamt hielten wir vier Vorträge auf der Konferenz. Michael Banck, technischer Leiter unseres Datenbank-Teams, präsentierte den deutschsprachigen Vortrag „PostgreSQL Performance Tuning“. Er gab einen tiefen und umfassenden Überblick über die wichtigsten Performance-Tuning-Parameter in PostgreSQL und erklärte, wie sie das Verhalten der Datenbank beeinflussen. Sein Vortrag zog ein großes Publikum an und wurde sehr gut aufgenommen.

  

Ich hatte die absolut einzigartige Gelegenheit, drei verschiedene Vorträge im englischen Track zu halten. In meinem regulären Vortrag „PostgreSQL Connections Memory Usage: How Much, Why and When“ präsentierte ich die Ergebnisse meiner Forschung und Tests zum Speicherverbrauch von PostgreSQL-Verbindungen. Nachdem ich die wichtigsten Aspekte des Linux-Speichermanagements und die Messungen des Speicherverbrauchs, die von Standardbefehlen gemeldet werden, erläutert hatte, beschrieb ich detailliert den Speicherverbrauch von PostgreSQL-Verbindungen während der Abfrageausführung basierend auf den Zahlen, die in smaps-Dateien gemeldet wurden. Ich plane hierzu detaillierte Blogbeiträge über meine Erkenntnisse zu veröffentlichen. Mein anderer Vortrag, „Building a Data Lakehouse with PostgreSQL“, wurde ursprünglich als Reservevortrag für den Fall einer kurzfristigen Absage ausgewählt. Der Vortrag „Creating a Board Game Chatbot with Postgres, AI, and RAG“ von Matt Cornillon musste ersetzt werden. Der Sprecher konnte nicht teilnehmen, da sein Flug sehr kurzfristig und unerwartet annulliert wurde.

 

Im Sponsor-Track präsentierten der CTO von credativ, Alexander Wirt, und ich einen Vortrag mit dem Titel „Your Data Deserves the Best: Migration to PostgreSQL“. Darin stellten wir unser neues Migrationstool „credativ-pg-migrator“ vor. Es ist in der Lage, Datenmodelle (Tabellen, Daten, Indizes, Constraints und Views) von Informix, IBM DB2 LUW, MS SQL Server, Sybase ASE, SQL Anywhere und MySQL/MariaDB zu migrieren. Im Falle von Informix kann es auch Stored Procedures, Funktionen und Trigger in PostgreSQL PL/pgSQL konvertieren. Weitere Details zu diesem Tool werden wir in einem separaten Blogbeitrag mitteilen.

Da es immer drei oder vier parallele Vorträge gab, musste ich sorgfältig auswählen, an welchen Sessions ich teilnehmen wollte. Besonders gefallen hat mir der Vortrag „András in Windowsland – a DBA’s (mis)adventures“ von András Váczi von Cybertec. Der Sprecher gab viele nützliche Tipps für den Zugriff auf und die Fehlerbehebung von PostgreSQL unter Windows. Mir gefiel auch der deutsche Vortrag „Modern VACUUM“ von Christoph Berg von Cybertec. Er gab wertvolle Einblicke in die Geschichte und Implementierungsdetails des VACUUM-Befehls und der Autovacuum-Hintergrundprozesse. Ein weiterer sehr interessanter Vortrag war die deutsche Präsentation „Modernes SSL ideal einsetzen“ von Peter Eisentraut von EDB. Der Vortrag behandelte die Auswahl geeigneter Protokollversionen und kryptografischer Cipher Suites, die Verwaltung von Schlüsseln und Zertifikaten sowie die Konfiguration von Client-/Server-Einstellungen zur Erfüllung zeitgemäßer Sicherheitsstandards. Der Vortrag „Comparing the Oracle and PostgreSQL transaction systems“ von Laurenz Albe von Cybertec erhielt viel wohlverdiente Aufmerksamkeit. Zu den Kernthemen gehörten der Undo/Redo-Mechanismus von Oracle im Vergleich zum MVCC-Ansatz von PostgreSQL, Unterschiede bei den Standard-Isolationsstufen und Anomalien sowie die Art und Weise, wie jede Datenbank Constraints und transaktionale DDL implementiert. Nicht zuletzt möchte ich den Vortrag „What is an SLRU anyway?“ erwähnen, der von einem wichtigen PostgreSQL-Contributor, Álvaro Herrera, gehalten wurde. Er erklärte, dass SLRUs im Wesentlichen zirkuläre Logs mit einem In-Memory-Cache sind, die zur Nachverfolgung von Informationen wie committeden Transaktionen oder Snapshot-Daten verwendet werden, und hob die Bedeutung der Innovationen in PostgreSQL 17 hervor, die SLRU-Cache-Größen konfigurierbar machten. Leider wurden die Vorträge nicht aufgezeichnet, aber die Folien für die Mehrheit der Vorträge sind bereits auf der Konferenz-Website verfügbar.

Die gesamte Veranstaltung war sehr informativ und bot hervorragende Möglichkeiten zum Networking. Wir freuen uns schon auf die nächste PGConf.DE. In der Zwischenzeit bleiben Sie auf dem Laufenden über alle Neuigkeiten der credativ und folgen Sie uns auf den sozialen Medien – LinkedIn und Mastodon.

Wenn Sie mehr über unsere Angebote zu PostgreSQL-Services wissen möchten, klicken Sie bitte hier!