TL;DR — Kurzzusammenfassung
PostgreSQL VACUUM und Autovacuum richtig tunen: Table Bloat beseitigen, XID-Wraparound verhindern und die Datenbankperformance in der Produktion maximieren.
PostgreSQL VACUUM ist eine der am meisten missverstandenen und gleichzeitig kritischsten Wartungsoperationen in jedem PostgreSQL-Deployment. Ohne ordentliches Tuning sammeln Tabellen still und leise tote Tupel an, die den Speicher aufblähen, die Abfrageleistung verschlechtern und im schlimmsten Fall einen katastrophalen XID-Wraparound auslösen, der die gesamte Datenbank offline nimmt. Dieser Leitfaden deckt alles ab, was Sie benötigen, um Autovacuum korrekt zu konfigurieren und Produktionsdatenbanken gesund zu halten.
Voraussetzungen
- PostgreSQL 13 oder höher (die meisten Beispiele gelten ab PostgreSQL 10+)
psqloder ein SQL-Client mit DBA-Zugang- Möglichkeit,
postgresql.confzu bearbeiten oderALTER SYSTEMzu verwenden - Grundlegende Vertrautheit mit PostgreSQL-Speicherkonzepten
Warum VACUUM existiert: MVCC und tote Tupel
PostgreSQL verwendet Multi-Version Concurrency Control (MVCC), um gleichzeitige Lese- und Schreibzugriffe ohne Sperren zu verwalten. Jedes UPDATE oder DELETE modifiziert Daten nicht an Ort und Stelle — stattdessen markiert es die alte Zeilenversion als tot und schreibt eine neue Version. Das macht Lesezugriffe nicht-blockierend, hinterlässt aber tote Tupel: Zeilenversionen, die für keine Transaktion mehr sichtbar sind, aber weiterhin Speicherplatz belegen.
Mit der Zeit verursachen tote Tupel drei ernste Probleme:
- Table Bloat: Die physische Tabellendatei wächst, selbst wenn die logische Zeilenanzahl konstant bleibt. Eine Tabelle mit 10 Millionen aktiven Zeilen könnte den Platz von 50 Millionen Zeilen auf der Festplatte belegen.
- Index Bloat: B-Tree-Indizes sammeln Zeiger auf tote Tupel an und erhöhen deren Größe und Scan-Kosten.
- XID-Wraparound: PostgreSQL verwendet 32-Bit-Transaktions-IDs. Nach etwa 2 Milliarden Transaktionen wickeln sich die IDs um. PostgreSQL erzwingt einen datenbankweiten Notstopp, wenn VACUUM alte Transaktionen nicht rechtzeitig einfrieren kann.
VACUUM löst alle drei Probleme, indem es Tabellenseiten scannt, tote Tupel als wiederverwendbaren Raum markiert und den Einfrierungshorizont für alte Transaktionen vorschiebt.
VACUUM-Typen im Vergleich
| Befehl | Sperre | Speicherrückgewinnung | Anwendungsfall |
|---|---|---|---|
VACUUM | Keine (teilt mit Lese-/Schreibzugriffen) | Markiert Platz zur Wiederverwendung, verkleinert Datei nicht | Routinewartung, Autovacuum |
VACUUM ANALYZE | Keine | Wie oben + aktualisiert Planer-Statistiken | Nach Masseninserts/-löschungen |
VACUUM FULL | Exklusiv (Tabelle gesperrt) | Schreibt Tabelle neu, gibt Platz an OS zurück | Einmalige Bloat-Bereinigung |
VACUUM FREEZE | Keine | Erzwingt Einfrieren aller alten XIDs | Wraparound-Prävention |
Verwenden Sie VACUUM FULL nur als einmaligen Bereinigungsschritt für stark aufgeblähte Tabellen — es hält eine exklusive Sperre, die alle Lese- und Schreibzugriffe für die Dauer blockiert. Für Online-Tabellen-Neuschreibungen verwenden Sie pg_repack.
Der Autovacuum-Daemon: Kernparameter
Autovacuum läuft als Hintergrund-Daemon, der Worker-Prozesse zum automatischen Vacuuming von Tabellen startet. Die Schlüsselparameter in postgresql.conf:
# Wie oft der Launcher nach Arbeit sucht
autovacuum_naptime = 1min
# Maximale gleichzeitige Autovacuum-Worker
autovacuum_max_workers = 3
# Löst Vacuum aus wenn tote Tupel > threshold + scale_factor * n_live_tup
autovacuum_vacuum_threshold = 50
autovacuum_vacuum_scale_factor = 0.2 # 20% der Tabelle
# Löst ANALYZE aus wenn geänderte Tupel dies überschreiten
autovacuum_analyze_threshold = 50
autovacuum_analyze_scale_factor = 0.1 # 10% der Tabelle
# Friert Zeilen ein, die so viele Transaktionen alt sind
autovacuum_freeze_max_age = 200000000
Der kritische Tuning-Punkt ist autovacuum_vacuum_scale_factor. Der Standardwert 0.2 bedeutet, dass Autovacuum auslöst, wenn 20% der Zeilen einer Tabelle tot sind. Bei einer 100-Zeilen-Tabelle ist das in Ordnung. Bei einer 50-Millionen-Zeilen-Tabelle löst Autovacuum nicht aus, bis 10 Millionen tote Tupel angesammelt sind.
Empfohlene globale Werte für Systeme mit hohem Schreibaufkommen:
autovacuum_vacuum_scale_factor = 0.05
autovacuum_analyze_scale_factor = 0.02
autovacuum_max_workers = 5
autovacuum_naptime = 30s
Tabellen-spezifische Autovacuum-Überschreibungen
Die leistungsfähigste Tuning-Technik ist das Setzen von Autovacuum-Parametern pro Tabelle:
-- Für eine Tabelle mit sehr hohem Schreibaufkommen (z.B. Events-/Audit-Log)
ALTER TABLE ereignisse SET (
autovacuum_vacuum_scale_factor = 0.01,
autovacuum_vacuum_threshold = 500,
autovacuum_analyze_scale_factor = 0.005,
autovacuum_vacuum_cost_delay = 2
);
-- Für eine nahezu statische Lookup-Tabelle (weniger aggressives Vacuum)
ALTER TABLE laendercodes SET (
autovacuum_vacuum_scale_factor = 0.5,
autovacuum_vacuum_threshold = 1000
);
Table Bloat überwachen
Mit pg_stat_user_tables
-- Top 20 Tabellen nach Anzahl toter Tupel
SELECT
schemaname,
relname AS tabellenname,
n_live_tup,
n_dead_tup,
round(n_dead_tup::numeric / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS pct_tot,
last_vacuum,
last_autovacuum,
last_analyze,
last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;
Tabellen nahe am XID-Wraparound erkennen
SELECT
schemaname,
relname,
age(relfrozenxid) AS xid_alter,
pg_size_pretty(pg_total_relation_size(oid)) AS tabellengroesse
FROM pg_class
JOIN pg_namespace ON pg_namespace.oid = relnamespace
WHERE relkind = 'r'
ORDER BY age(relfrozenxid) DESC
LIMIT 20;
Jede Tabelle mit xid_alter über 1,5 Milliarden erfordert sofortige Aufmerksamkeit.
Tatsächlichen Bloat mit pgstattuple messen
CREATE EXTENSION IF NOT EXISTS pgstattuple;
SELECT
dead_tuple_count,
round(dead_tuple_percent, 1) AS pct_tot,
free_space,
round(free_percent, 1) AS pct_frei
FROM pgstattuple('public.bestellungen');
XID-Wraparound-Prävention
Überwachen Sie das Alter auf Datenbankebene:
SELECT
datname,
age(datfrozenxid) AS xid_alter,
pg_size_pretty(pg_database_size(datname)) AS db_groesse
FROM pg_database
ORDER BY age(datfrozenxid) DESC;
Notfallwiederherstellung wenn eine Datenbank nahe am Wraparound ist:
vacuumdb --all --freeze --verbose --analyze
VACUUM FULL vs pg_repack
Für Produktionssysteme, die keine Ausfallzeit tolerieren können, verwenden Sie pg_repack:
sudo apt install postgresql-16-repack
pg_repack -h localhost -U postgres -d meinedb -t bestellungen
pg_repack -h localhost -U postgres -d meinedb --only-indexes -t bestellungen
Häufige Probleme und Lösungen
Autovacuum durch lang laufende Transaktionen abgebrochen: Veraltete Transaktionen suchen:
SELECT pid, now() - xact_start AS dauer, query, state
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY dauer DESC
LIMIT 10;
“Autovacuum: preventing wraparound” in den Logs: Autovacuum ist im Notfallmodus und ignoriert das Cost-Throttling. Diese Vacuum-Prozesse nicht blockieren oder beenden.
Tuning-Referenz nach Datenbankgröße
| DB-Größe | autovacuum_max_workers | autovacuum_vacuum_scale_factor | autovacuum_vacuum_cost_limit |
|---|---|---|---|
| < 10 GB | 3 (Standard) | 0.05 | 200 (Standard) |
| 10–100 GB | 4–5 | 0.02 | 400 |
| 100 GB – 1 TB | 6–8 | 0.01 | 600 |
| > 1 TB | 8–10 + pro Tabelle | 0.005 bei aktiven Tabellen | 800 |
Datenbankvergleich
| Funktion | PostgreSQL VACUUM | MySQL OPTIMIZE TABLE | SQL Server Index Rebuild | Oracle Segment Shrink |
|---|---|---|---|---|
| Benötigte Sperren | Keine (regulär), Exklusiv (FULL) | Exklusiv | Online (Enterprise) | Zeilen-exklusiv |
| Online-Neuschreib-Tool | pg_repack | pt-online-schema-change | Online-Rebuild | SHRINK SPACE COMPACT |
| Automatisch | Autovacuum-Daemon | Manuell oder Event-Scheduler | Automatische Index-Wartung | Nicht automatisch |
| Wraparound-Risiko | Ja (XID) | Nein | Nein | Nein |
| Bloat-Sichtbarkeit | pg_stat_user_tables, pgstattuple | information_schema | sys.dm_db_index_physical_stats | DBA_SEGMENTS |
Zusammenfassung
- VACUUM gewinnt Platz von toten Tupeln aus MVCC-Updates und -Löschungen zurück; ohne es blähen sich Tabellen auf und XID-Wraparound hält die Datenbank schließlich an.
- Autovacuum ist die automatische Lösung, benötigt aber Tuning — der Standard
autovacuum_vacuum_scale_factor = 0.2ist für große Tabellen zu hoch. - Verwenden Sie tabellen-spezifische Konfiguration mit
ALTER TABLE SETfür Tabellen mit hohem Schreibaufkommen. - Überwachen Sie
pg_stat_user_tablesundage(datfrozenxid)inpg_database. - Verwenden Sie pg_repack statt
VACUUM FULLauf Produktionstabellen um Ausfallzeiten zu vermeiden. - Halten Sie
age(datfrozenxid)deutlich unter 1,5 Milliarden auf allen Datenbanken.