pg_prewarm - Turbostart für den Postgres Buffer Cache
Die Extension enthält noch zwei Konfigurationsparameter für die pospg_prewarm.autoprewarm (boolean): Ermöglicht das das An-/Abschalten von pg_prewarm ohne das Einträge aus der postgresql.conf entfernt werden müssen. Der Standardwert ist “on” und der Parameter kann nur beim Serverstart gesetzt werden.
- pg_prewarm.autoprewarm_interval (int): Intervall der Updates der autoprewarm.blocks Datei in Sekunden. Der Standardwert sind 300 Sekunden. Beim Einstellen des Werts 0 wird nur beim Herunterfahren der PostgreSQL Instanz ein Dump erzeugt.
Wenn die Extension im laufenden Betrieb des Datenbankclusters aktiviert wurde, bieten die Funktionen autoprewarm_start_worker(), das nachträgliche Starten des background worker Prozesses, sowie autoprewarm_dump_now() das Aktualisieren der autoprewarm.blocks Datei.
Zuletzt kann die Aufnahme von Tabellen und deren Blöcken auch über die Funktion pg_prewarm(...) gesteuert werden:
pg_prewarm(regclass,
mode text default 'buffer',
fork text default 'main',
first_block int8 default null,
last_block int8 default null)
- regclass: Name der Tabelle
- mode: Es gibt 3 unterschiedliche Ladestrategien
- prefetch: Asynchrones Laden per Betriebssystemaufruf
- read: Liest die Liste der definierten Datenblöcke und lädt im Gegensatz zu prefetch synchron, aber eventuell langsamer
- buffer: Liest die Datenblöcke in den Datenbank Buffer Cache
- fork: Normalerweise main für die Hauptdatendatei
- first_block: Erster zu lesender Block, NULL synonym für 0, also Beginn der Tabelle
- last_block: Letzter zu lesender Block, NULL bis Ende der Tabelle
Anmerkung: Mehr Informationen zu dem Parameter fork und seiner Bedeutung ist in der Postgres Dokumentation zu finden.
Zum Testen wird docker-compose mit Postgres v12 verwendet. In einem ersten Test wird dabei pg_prewarm nicht verwendet.
version: "3.8"
services:
db:
image: "postgres:latest"
container_name: "pg12"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
ports:
- "5432:5432"
command:
- "postgres"
- "-c"
- "shared_buffers=4GB"
volumes:
- pg12_data:/var/lib/postgresql/data
volumes:
pg12_data:
Der Container wird mit docker-compose gestartet und danach wird sich per docker exec auf den Container geschaltet.
docker-compose up -d
docker exec -it pg12 bash
Nun wird eine Datenbank testdb erzeugt und pgbench initialisiert. Dabei ist die Skalierung so gewählt, dass die Datenbank komplett in die Shared Buffers geladen werden kann.
psql -U postgres -c "CREATE DATABASE testdb;"
CREATE DATABASE
pgbench -U postgres -i -s 250 testdb
dropping old tables...
creating tables...
generating data...
100000 of 20000000 tuples (0%) done (elapsed 0.06 s, remaining 11.84 s)
200000 of 20000000 tuples (1%) done (elapsed 0.18 s, remaining 17.48 s)
...
19900000 of 20000000 tuples (99%) done (elapsed 19.24 s, remaining 0.10 s)
20000000 of 20000000 tuples (100%) done (elapsed 19.34 s, remaining 0.00 s)
vacuuming...
creating primary keys...
done.
psql -U postgres -d testdb -c "\l+ testdb"
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges | Size | Tablespace | Description
--------+----------+----------+------------+------------+-------------------+---------+------------+-------------
testdb | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | 3746 MB | pg_default |
(1 row)
Der eigentliche Test kann jetzt beginnen. Dazu wird der Container gestoppt, erneut gestartet und in den Container verbunden.
docker-compose down
docker-compose up -d
docker exec -it pg12 bash
Per pgbench werden die TPS ermittelt. Es wird ein Client simuliert, der 10 Minuten lang nur selects ausführt. pgbench gibt den Fortschritt inklusive TPS-Wert alle 5 Sekunden aus. Dieser wird in die Logdatei pgbench.log geschrieben.
pgbench -U postgres -c 1 -S -T 600 -P 5 testdb > pgbench.log 2>&1
Als nächstes wird die docker-compose Datei um pg_prewarm als Startparameter erweitert.
version: "3.8"
services:
db:
image: "postgres:latest"
container_name: "pg12_with_prewarm"
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
ports:
- "5432:5432"
command:
- "postgres"
- "-c"
- "shared_buffers=4GB"
- "-c"
- "shared_preload_libraries=pg_prewarm"
volumes:
- pg12_prewarm_data:/var/lib/postgresql/data
volumes:
pg12_prewarm_data:
Der Container wird gestartet.
docker-compose up -d
docker exec -it pg12_with_prewarm bash
Als erstes lässt sich der zu Beginn beschriebene Prozess zu pg_prewarm anzeigen.
ps -aef | grep autoprewarm
postgres 31 1 0 08:02 ? 00:00:00 postgres: autoprewarm master
Wie bei dem ersten Container wird die Datenbank testdb erzeugt und danach pg_prewarm aktiviert. Die pgbench Tabellen werden explizit geladen und anschließend mit autoprewarm_dump_now gedumpt.
psql -U postgres -d testdb -c "CREATE EXTENSION pg_prewarm;"
CREATE EXTENSION
psql -U postgres -d testdb -c "SELECT * FROM pg_prewarm('public.pgbench_accounts');"
pg_prewarm
------------
409837
(1 row)
psql -U postgres -d testdb -c "SELECT * FROM pg_prewarm('public.pgbench_branches');"
pg_prewarm
------------
2
(1 row)
psql -U postgres -d testdb -c "SELECT * FROM pg_prewarm('public.pgbench_tellers');"
pg_prewarm
------------
14
(1 row)
psql -U postgres -d testdb -c "SELECT * FROM autoprewarm_dump_now();"
autoprewarm_dump_now
----------------------
410382
(1 row)
Der eigentliche Test kann jetzt beginnen. Dazu wird der Container gestoppt, erneut gestartet und mit dem Container verbunden.
docker-compose down
docker-compose up -d
docker exec -it pg12_with_prewarm bash
Per pgbench werden wieder die TPS Werte ermittelt.
pgbench -U postgres -c 1 -S -T 600 -P 5 testdb > pgbench.log 2>&1

Abbildung: Vergleich der TPS Werte
Beim Vergleich der TPS Werte ohne und mit pg_prewarm zeigen sich zu Beginn der Aufzeichnungen deutliche Unterschiede. Ohne pg_prewarm dauert es fast 2 Minuten lang bis die TPS Werte ein ähnliches Niveau wie mit pg_prewarm erreichen. Das liegt daran, dass die Data Pages erst bei Abfrage aus dem Dateisystem in den PostgreSQL Buffer Cache geladen werden. Daraus ist zu schließen, dass pg_prewarm bei (sehr) großen Datenbankclustern mit viel RAM einen deutlichen Vorteil bringt.
Die Extension hilft so bei unterschiedlichsten Anwendungsfällen. Dazu gehört an erster Stelle die bessere Reaktionszeit auf User-Anfragen nach einem Neustart des Datenbankclusters. Auf der anderen Seite kann pg_prewarm auch bei Tests genutzt werden, um authentische Zustände im Buffer Cache zu generieren, wie sie auch in Produktion zu finden sind.