11. Okt. 2025·8 Min. Lesezeit

Postgres-Bloat und Autovacuum-Tuning für schnellere Performance

Postgres-Bloat und Autovacuum-Tuning: Erfahren Sie, wie Sie Tabellen- und Index-Bloat erkennen, Autovacuum sicher anpassen und Wartung planen, um die Performance wiederherzustellen.

Postgres-Bloat und Autovacuum-Tuning für schnellere Performance

Was Postgres-Bloat ist und warum Autovacuum wichtig ist

Bloat ist der zusätzliche, verschwendete Platz in Postgres-Tabellen und -Indizes, der sich über die Zeit aufbaut. Er entsteht meist durch eine konstante Reihe von UPDATEs und DELETEs. Postgres überschreibt Zeilen nicht an Ort und Stelle: es schreibt eine neue Version und lässt die alte zurück, bis die Bereinigung stattfindet.

Dieser verschwendete Platz schadet der Performance auf einige vorhersehbare Arten. Abfragen müssen mehr Pages von Festplatte und Arbeitsspeicher lesen, um dieselbe Menge echter Daten zu finden. Indizes werden größer, sodass Lookups und Joins mehr Arbeit erfordern. Auch Schreibvorgänge können langsamer werden, weil Postgres größere Indizes pflegen und mehr Pages anfassen muss. Backups und Restores dauern ebenfalls länger, weil einfach mehr kopiert werden muss.

Autovacuum ist der Hintergrundprozess, der das in Schach hält. Er hat hauptsächlich zwei Aufgaben:

  • Bereinigt tote Zeilenversionen, damit ihr Platz wiederverwendet werden kann (VACUUM)
  • Aktualisiert Planungsstatistiken, damit Postgres gute Abfragepläne wählt (ANALYZE)

Ein wichtiger Punkt: Autovacuum verkleinert normalerweise nicht die physische Datei auf der Festplatte. Es macht Platz innerhalb der Tabelle wiederverwendbar, aber die tatsächliche Datenträgernutzung sinkt möglicherweise nicht. Um Platz an das OS zurückzugeben, sind meist aufwendigere Maßnahmen nötig (wie VACUUM FULL oder ein Tabellen-Rebuild), und diese Optionen haben echte Trade-offs.

Ein einfaches Beispiel: Ein KI-generiertes Admin-Panel aktualisiert Benutzerdaten bei jedem Seitenaufruf und erzeugt so konstant tote Zeilen. Autovacuum kann das für eine Weile kompensieren. Dann kommt ein Traffic-Spike, es gerät in Rückstand, und plötzlich sind häufige Abfragen träge, obwohl die Datensatzzahl kaum gestiegen ist.

Autovacuum-Tuning ist kein Einmal-Schalter. Die richtigen Einstellungen hängen von Ihrer Schreibrate, der Tabellengröße und davon ab, wie empfindlich Sie gegenüber Hintergrundarbeit sind. Der sicherste Ansatz ist iterativ: messen, kleine Änderungen vornehmen, die Auswirkungen beobachten und dann anpassen.

Häufige Anzeichen für Bloat in Ihrer Datenbank

Bloat zeigt sich oft als „nichts hat sich geändert, aber alles ist langsamer geworden.“ Die Geschäftsdaten wirken stabil, dennoch muss Postgres mehr Pages lesen und filtern, um dieselben Abfragen zu beantworten. Diese zusätzliche Arbeit wird zu mehr IO, mehr CPU und mehr Wartezeiten.

Signale, die oft auf Bloat hinweisen, sind:

  • Abfragen, die letzte Woche noch schnell waren, ziehen sich jetzt, besonders solche, die früher schnelle Index-Scans machten.
  • Die Festplattennutzung steigt weiter, obwohl die Geschäftsdaten nicht im gleichen Maße wachsen.
  • Höhere Leselatenz und mehr Cache-Misses (mehr Lesezugriffe von der Festplatte statt aus dem Speicher).
  • Autovacuum läuft oft, aber Sie sehen nicht die übliche Erholung danach.
  • Backups und Restores dauern länger, und Replikations-Lag taucht in Spitzenzeiten auf.

Ein feiner Hinweis ist, wenn der Abfrageplan weiterhin „richtig“ aussieht (er benutzt noch den erwarteten Index), die Laufzeit aber deutlich schlechter ist. Das kann passieren, wenn der Index aufgebläht ist und größer als nötig, sodass Postgres mehr Page-Reads benötigt, um ihn zu durchlaufen.

Ein anderes Muster: Table-Scans werden langsamer, obwohl die Zeilenzahlen ähnlich bleiben. Updates und Deletes hinterlassen tote Tupel, und wenn die Bereinigung nicht nachkommt, wächst die Tabelle in Pages. Postgres hat dann mehr Pages zu durchsuchen und mehr Visibility-Checks durchzuführen.

Ein praktisches Beispiel: Eine kleine SaaS-App schreibt bei jeder Anfrage ein "last_seen"-Feld. Während der Spitzenzeiten beginnen Leseanfragen zu timenouten, die Festplattennutzung steigt, und Autovacuum ist den ganzen Tag beschäftigt. Das Grundproblem ist nicht eine einzelne schlechte Abfrage, sondern die Datenbank, die zusätzliche Arbeit macht, weil tote Zeilen und übergroße Indizes sich angesammelt haben.

Wenn Sie zwei oder mehr dieser Anzeichen gleichzeitig sehen, messen Sie Bloat, bevor Sie Einstellungen ändern. Autovacuum-Tweaks helfen am meisten, wenn Sie bestätigen können, welche Tabellen und Indizes tatsächlich wachsen.

Kurze Checkliste: Bloat bestätigen, bevor Sie tunen

Bevor Sie etwas ändern, vergewissern Sie sich, dass Sie wirklich Bloat haben und nicht einen schlechten Abfrageplan, fehlende Indizes oder eine Transaktion, die die Bereinigung blockiert. Tuning funktioniert am besten, wenn Sie ein paar konkrete Tabellen und Indizes benennen können.

Beginnen Sie damit, schnelle, einfache Zahlen zu sammeln, die Sie Woche für Woche vergleichen können. Konzentrieren Sie sich auf:

  • Die größten Tabellen und ob sie wirklich viele Schreibvorgänge sehen (kalte Tabellen erklären selten Bloat).
  • Die Autovacuum-Historie pro Tabelle: wann sie zuletzt lief, wie oft und ob die Dauer im Zeitverlauf zunimmt.
  • Hohe Dead-Tuple-Zahlen, besonders bei häufig aktualisierten Tabellen.
  • Große Indizes auf stark veränderten Spalten (Indizes können aufblähen, auch wenn die Tabelle in Ordnung aussieht).
  • Lang laufende Transaktionen, denn sie verhindern, dass VACUUM tote Tupel entfernt.

Wenn Sie einen schnellen ersten Eindruck wollen, liefern diese Abfragen meist genug Signal, um das weitere Vorgehen zu bestimmen:

-- Biggest tables
SELECT relname, pg_total_relation_size(relid) AS bytes
FROM pg_catalog.pg_statio_user_tables
ORDER BY bytes DESC
LIMIT 5;

-- Dead tuples and last vacuum/autovacuum
SELECT relname, n_live_tup, n_dead_tup, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;

-- Biggest indexes
SELECT indexrelname, pg_relation_size(indexrelid) AS bytes
FROM pg_stat_user_indexes
ORDER BY bytes DESC
LIMIT 10;

-- Long-running transactions (can block cleanup)
SELECT pid, now() - xact_start AS xact_age, state
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY xact_age DESC
LIMIT 10;

Eine praktische Lesart der Ergebnisse: Wenn eine Events-ähnliche Tabelle riesig ist, eine steigende Anzahl toter Tupel hat und Autovacuum zwar läuft, aber nie aufholt, ist diese Tabelle ein Kandidat fürs Tuning. Wenn tote Tupel in vielen Tabellen hoch sind und Sie gleichzeitig eine Transaktion sehen, die seit Stunden läuft, beheben Sie zuerst die lange Transaktion. Autovacuum kann nicht gegen eine nie endende Transaktion anarbeiten.

Wie man Tabelle- und Index-Bloat misst (praktische Methoden)

Beginnen Sie mit dem, was Postgres bereits verfolgt. Bloat korreliert oft mit vielen toten Zeilen (dead tuples) und starkem Churn (Updates und Deletes) in denselben Tabellen.

1) Eingebaute Statistiken nutzen, um tote Tupel und Churn zu finden

Diese Views sind ein schneller, risikoarmer erster Schritt. Suchen Sie nach Tabellen, bei denen n_dead_tup hoch ist und n_tup_upd sowie n_tup_del kontinuierlich steigen.

-- Tables with lots of dead tuples
SELECT
  schemaname,
  relname,
  n_live_tup,
  n_dead_tup,
  last_autovacuum,
  last_vacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;

-- Churn (writes) that tends to create bloat
SELECT
  schemaname,
  relname,
  n_tup_ins,
  n_tup_upd,
  n_tup_del,
  n_tup_hot_upd
FROM pg_stat_user_tables
ORDER BY (n_tup_upd + n_tup_del) DESC
LIMIT 20;

Eine Faustregel: Wenn tote Tupel einen großen Anteil der Live-Tupel ausmachen und Autovacuum selten läuft (oder lange braucht), haben Sie wahrscheinlich Table-Bloat und sollten Autovacuum-Einstellungen sowie mögliche Blocker untersuchen.

2) Bloat ohne schwere Mathematik schätzen

Praktisch vorgehen: Vergleichen Sie, wie groß die Relation auf der Festplatte ist gegenüber der Anzahl der Live-Zeilen. Ist eine Tabelle riesig, aber die Live-Zeilen sind es nicht, ist die Lücke oft Bloat oder sehr breite Zeilen. Sie können das später mit tieferen Tools bestätigen.

Prüfen Sie außerdem, ob die Tabelle schnell gewachsen ist und nie wieder geschrumpft wurde. Postgres gibt nach normalen Deletes und Updates nicht automatisch Platz an das OS zurück, weshalb Bloat und Autovacuum oft zusammen diskutiert werden.

3) Index-Größe vs. Nutzen prüfen

Indizes können ebenfalls aufblähen, und ungenutzte Indizes sind häufige, versteckte Kosten. Vergleichen Sie Größe und wie oft Postgres den Index scanned.

SELECT
  s.schemaname,
  s.relname AS table_name,
  s.indexrelname AS index_name,
  pg_relation_size(s.indexrelid) AS index_bytes,
  s.idx_scan
FROM pg_stat_user_indexes s
ORDER BY pg_relation_size(s.indexrelid) DESC;

Wenn ein Index groß ist und idx_scan unter realem Traffic nahe null bleibt, ist er ein Kandidat für Entfernung oder Redesign. Vergewissern Sie sich, dass er nicht für Constraints, Einzigartigkeit oder eine seltene, aber kritische Abfrage erforderlich ist, bevor Sie ihn entfernen.

4) Wann tiefere Inspektions-Tools nutzen

Wenn Sie echte Bloat-Zahlen benötigen, nutzen Sie pgstattuple (oder pgstattuple_approx für große Tabellen). Es kann toten Platz und Tupeldichte melden, ist aber schwerer als die Stats-Views und sollte bei geringem Traffic laufen.

5) Basislinie notieren

Bevor Sie etwas ändern, machen Sie einen Snapshot: Top-Tabellen nach n_dead_tup, Top-Indizes nach Größe und ein paar Schlüsselabfragezeiten. Nach dem Tuning und der Wartung wissen Sie so, was sich tatsächlich verbessert hat.

Warum Autovacuum in realen Apps in Rückstand gerät

Beende den Update-Sturm
Wir reparieren KI-erstellte Apps, die UPDATEs und DELETEs spammen, damit Autovacuum mithalten kann.

Autovacuum ist Postgres’ Hintergrund-Reinigungstrupp. Es entfernt tote Zeilen, die durch Updates und Deletes entstehen, und hält Statistiken aktuell, damit Pläne sinnvoll bleiben. Es entscheidet, wann es laufen soll, mithilfe von Schwellenwerten, die teils fest sind und teils von der Tabellengröße abhängen.

Für jede Tabelle wartet Postgres, bis die Anzahl geänderter Zeilen eine Auslösung überschreitet wie:

  • eine Basisanzahl (autovacuum_vacuum_threshold)
  • plus ein Prozentsatz der Tabelle (autovacuum_vacuum_scale_factor)

Das funktioniert für viele Tabellen gut. Aber stark aktualisierte Tabellen (Sessions, Events, Job-Queues) können tote Zeilen schneller erzeugen, als diese Trigger feuern — besonders wenn der Scale-Factor zu hoch ist. Die Tabelle wächst, der Trigger wächst mit, und VACUUM kommt zu spät.

Reale Blocker, die Bereinigung stoppen

Der häufigste Grund, warum VACUUM tote Zeilen nicht entfernen kann, ist eine lange laufende Transaktion. Wenn irgendeine Transaktion offen bleibt, muss Postgres alte Row-Versionen behalten, selbst wenn alle anderen fertig sind. Das passiert durch idle-in-transaction Sessions (ein Tab offen), einen hängenden Worker oder einen Batch-Job, der zu viel Arbeit in einer Transaktion zusammenfasst.

Selbst wenn VACUUM laufen darf, kann es zu sanft konfiguriert sein. Autovacuum hat Kosten-Limits (wie viel IO es nutzen darf, bevor es schläft). Auf stark ausgelasteten Systemen sind diese Limits oft konservativ, sodass VACUUM langsam vorankommt, während Ihre App weiter tote Zeilen erzeugt.

Versteckte Ursachen, die es verschlimmern

Einige Muster schieben Autovacuum regelmäßig in Rückstand: große Batch-Updates/Deletes, die viele Zeilen auf einmal berühren; heiße Tabellen mit häufigen UPDATEs (auch kleine); idle-in-transaction Verbindungen, die Snapshots halten; lang laufende Reports, die Transaktionen offenhalten; und bursty Traffic, bei dem VACUUM zwischen Spitzen nicht aufholen kann.

Wenn Sie Bloat untersuchen, finden Sie zuerst zwei Dinge: die Tabelle mit dem meisten Churn und die längste Transaktion, die nie zu enden scheint. Beheben Sie diese zuerst, dann tune die Einstellungen.

Schritt-für-Schritt: Autovacuum sicher tunen (klein anfangen)

Der sicherste Weg, Autovacuum zu tunen, ist: ändern Sie eine Einstellung, an einer Tabelle, und beobachten Sie sie einen ganzen Tag. Die meisten Autovacuum-Probleme kommen von wenigen, stark churnenden Tabellen (Sessions, Events, Logs, Queue-Tabellen), nicht von der gesamten Datenbank.

Wählen Sie eine Tabelle, die viele Updates oder Deletes hat und bereits Verlangsamungen zeigt. Ein guter Kandidat hat meist schnell steigende n_dead_tup-Werte und während normalem Traffic viele Schreibzugriffe.

Eine kleine, kontrollierte Abfolge, die gut funktioniert:

  1. Wählen Sie eine stark churnende Tabelle und notieren Sie eine Basislinie: Zeilenzahl, tote Tupel und wie oft Autovacuum läuft.
  2. Senken Sie die pro-Table Vacuum- und Analyse-Scale-Factors, damit die Wartung früher läuft (bevor die Tabelle riesig wird). Halten Sie die Änderung moderat.
  3. Wenn Vacuum zu langsam ist, passen Sie Kapazitäten vorsichtig an: erhöhen Sie autovacuum_max_workers etwas, oder heben Sie autovacuum_vacuum_cost_limit an, sodass Vacuum Fortschritt macht, ohne die App zu erdrücken.
  4. Bestätigen Sie, dass ANALYZE die Pläne aktuell hält, indem Sie weniger unerwartete Planwechsel und stabilere Abfragezeiten beobachten.
  5. Überwachen Sie einen vollen Tag mit realem Traffic und vergleichen Sie: tote Tupel, Vacuum-Frequenz, Abfrage-Latenz sowie CPU/IO.

Eine praktische per-Table-Änderung sieht oft so aus:

ALTER TABLE public.events SET (
  autovacuum_vacuum_scale_factor = 0.02,
  autovacuum_analyze_scale_factor = 0.01,
  autovacuum_vacuum_threshold = 1000,
  autovacuum_analyze_threshold = 1000
);

Dann validieren Sie mit den Ihnen bereits bekannten Statistiken:

SELECT relname, n_live_tup, n_dead_tup, last_autovacuum, last_autoanalyze
FROM pg_stat_all_tables
WHERE schemaname = 'public'
ORDER BY n_dead_tup DESC
LIMIT 10;

Beispiel: Eine App schreibt 5 Millionen Event-Zeilen pro Tag und löscht alte stündlich. Mit Default-Einstellungen startet VACUUM zu spät und tote Zeilen häufen sich. Indem man die Scale-Factors nur für events senkt, läuft Autovacuum häufiger, Abfragezeiten bleiben stabiler, und man vermeidet riskante, aufwendige Wartungsaktionen in Spitzenzeiten.

Wenn Sie ein KI-generiertes Projekt geerbt haben, bei dem das Schreibverhalten chaotisch ist (Retry-Loops, doppelte Inserts, Update-Stürme), lohnt es sich, die Ursache zu beheben. Sonst tunen Sie Autovacuum nur, um fehlerhaftes Verhalten zu kompensieren.

Schritt-für-Schritt: Platz und Geschwindigkeit zurückgewinnen, ohne die Verfügbarkeit zu zerstören

Beginnen Sie mit den am wenigsten störenden Werkzeugen. Meist erzielen Sie spürbare Verbesserungen, ohne Writes zu blockieren oder die App herunterzufahren.

1) Normales VACUUM ausführen (meist sicher und oft ausreichend)

Ein normales VACUUM entfernt tote Row-Versionen, sodass Postgres den Platz wiederverwenden kann. Es aktualisiert auch Sichtbarkeitsinformationen, sodass Index- und Sequenzscans schneller werden. Es schrumpft die Datei auf der Festplatte nicht, aber es stoppt oft das weitere Anwachsen und verbessert die Abfragegeschwindigkeit.

Falls möglich, machen Sie es in kleineren Schritten (eine große Tabelle nach der anderen) während geringer Last. Beispiel:

VACUUM (ANALYZE) public.big_table;

2) Aufgeblähte Indizes rebuilden (wenn Abfragen weiterhin langsam sind)

Wenn ein Tabellen-VACUUM nur wenig geholfen hat, sind oft Indizes das Hauptproblem. Aufgeblähte Indizes machen Lesevorgänge langsamer und erhöhen zufälliges IO. Für Produktionssysteme bevorzugen Sie ein Concurrent-Rebuild, damit Lesen und Schreiben weiterlaufen:

REINDEX INDEX CONCURRENTLY public.big_table_some_idx;

Erwarten Sie, dass das Zeit und zusätzlichen temporären Festplattenspeicher benötigt, während der neue Index gebaut wird.

3) VACUUM FULL nur bei akzeptierter Downtime

VACUUM FULL schreibt die ganze Tabelle neu und kann Platz an das OS zurückgeben. Es ist disruptiv, weil es einen exklusiven Lock auf die Tabelle nimmt. Das bedeutet, Inserts, Updates und oft auch Reads werden blockiert.

Nutzen Sie es nur, wenn Sie ein klares Wartungsfenster haben und der Festplattenspeicher wirklich knapp ist.

4) Sicherere Optionen für sehr große Tabellen

Wenn eine Tabelle sehr groß ist und Sie Platz ohne lange Sperren zurückgewinnen müssen, planen Sie ein Wartungsfenster und nutzen Sie eine dieser Strategien: rebuilden Sie zuerst die schlimmsten Indizes mit REINDEX CONCURRENTLY, überlegen Sie Partitionierung oder Archivierung alter Daten und das Löschen alter Partitionen, oder führen Sie ein Tabellen-Rewrite (kopieren in eine neue Tabelle und Swap) durch, wenn Sie den Cutover kontrollieren können.

5) Überprüfen, dass sich wirklich etwas verbessert hat

Verlassen Sie sich nicht auf ein Gefühl. Vergleichen Sie vorher und nachher mit einfachen Checks:

SELECT relname, n_dead_tup, last_vacuum, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;

Prüfen Sie außerdem erneut Größen und Abfragezeiten. Wenn Bloat schnell zurückkehrt, ist die Ursache oft App-Verhalten (riesige Updates, fehlende Indizes, lange Transaktionen).

Ein Wartungsplan, der Bloat fernhält

Geschwindigkeit ohne Downtime zurückgewinnen
Wir begleiten einen risikoarmen Plan: pro-Tabellen-Tuning, sichere Reindex-Schritte und Validierung der Ergebnisse.

Ein guter Wartungsplan ist absichtlich unspektakulär. Er macht Bloat zur Routine statt zu einer Überraschungs-Störung. Das Ziel ist kein einmaliger Gewinn, sondern ein stetiger Rhythmus, der zum Schreibverhalten Ihrer App passt.

Wählen Sie Wartungsfenster nach realem Traffic. Schauen Sie sich die geschäftigsten 30 bis 60 Minuten pro Tag an und meiden Sie diese. Falls Sie keine Grafiken haben, nutzen Sie einfache Hinweise: Kassiervorgänge, geplante Batch-Jobs, Marketing-Sendungen und Support-Spitzen.

Ein einfacher wöchentlicher Rhythmus, der für viele Teams funktioniert:

  • Prüfen Sie die fünf größten Tabellen und Indizes. Kontrollieren Sie tote Tupel und letzte Vacuum-Zeiten.
  • Führen Sie manuelle VACUUM (ANALYZE)-Läufe auf den heißesten Tabellen aus, wenn Autovacuum regelmäßig hinten liegt.
  • Prüfen Sie auf lang laufende Transaktionen, die Vacuum blockieren.

Einmal im Monat fügen Sie einen schwereren Durchlauf hinzu, wenn der Traffic am niedrigsten ist. Autovacuum sollte normalen Churn bewältigen. Manuelle Eingriffe sind für Ausnahmen: burstige Tabellen, große Delete-Jobs und Schema-Änderungen.

Für die monatliche Checkliste kurz und messbar bleiben:

  • Vergleichen Sie Tabellen- und Index-Wachstum mit dem Vormonat.
  • Prüfen Sie, ob die Vacuum-Dauer steigt.
  • Entscheiden Sie, ob eine Tabelle eigene Autovacuum-Einstellungen braucht (Schwellen, Scale-Factors, Cost-Limits).
  • Planen Sie gezielte REINDEXe (oder kontrollierte Rebuilds) nur, wenn Sie die zusätzliche Last tolerieren können.

Verfolgen Sie einige Kennzahlen über die Zeit: tote Tupel (n_dead_tup), Tabellen- vs. Gesamtgröße einschließlich TOAST, Indexgrößen und Vacuum/Analyze-Zeitstempel. Wenn Sie neue Features ausrollen, planen Sie vorab. Jede neue stark schreibende Tabelle sollte mit sinnvollen Autovacuum-Einstellungen starten und einen Platz in Ihrer wöchentlichen Überprüfung haben.

Häufige Fehler, die Bloat und Performance verschlechtern

Die meisten Bloat-Probleme verschlimmern sich, weil die „Lösung“ zu breit oder zu schnell angewendet wird. Besser ist, eine Änderung, messen, dann die nächste.

1) Autovacuum zu stark hochdrehen und IO-Stürme erzeugen

Autovacuum global auf maximale Werte zu stellen, kann Festplatten-Reads und -Writes in die Höhe treiben. Das kann Nutzerabfragen verlangsamen, Latenz erhöhen und trotzdem das eigentliche Problem nicht lösen, wenn es nur ein paar heiße Tabellen sind.

Sicherer ist, pro Tabelle für die wenigen stark churnenden Tabellen zu tunen und die Auswirkungen zu beobachten.

2) VACUUM FULL auf einer beschäftigten Tabelle ohne Plan nutzen

VACUUM FULL kann Platz zurückgewinnen, aber es schreibt die Tabelle neu und blockiert normalen Zugriff. In Produktion kann das wie ein Ausfall aussehen.

Wenn Sie großen Platzbedarf haben, planen Sie ein Wartungsfenster, prüfen Sie alternative Ansätze und stellen Sie sicher, dass genug freier Speicher für das Rewrite verfügbar ist.

3) Lange Transaktionen ignorieren und Autovacuum die Schuld geben

Autovacuum kann keine toten Zeilen entfernen, wenn eine lange Transaktion alte Snapshots offenhält. Sie sehen tote Tupel anschwellen, selbst wenn Autovacuum konstant läuft.

Häufige Ursachen sind verwaiste Admin-Sessions, Hintergrundjobs, die nicht committen, oder App-Code, der eine Transaktion öffnet und dann langsame Arbeit darin macht.

4) Nur globale Einstellungen ändern statt Hotspots zu reparieren

Globale Autovacuum-Settings sind ein grobes Werkzeug. Bloat konzentriert sich meist in spezifischen Tabellen und Indizes, die ihre eigenen Schwellen und Cost-Settings brauchen.

Wenn Sie global tunen, vergeuden Sie Ressourcen an kalte Tabellen, während die eigentliche Problem-Tabelle weiter wächst.

5) Einmal messen, fünf Dinge ändern und Basislinie verlieren

Wenn Sie mehrere Regler gleichzeitig verstellen, können Sie nicht erkennen, was geholfen hat. Behalten Sie eine einfache Basislinie (Tabellengrößen, tote Tupel, Vacuum/Analyze-Zeiten, Abfrage-Latenz) und ändern Sie jeweils nur eine Variable.

Beispiel: Von langsamen Abfragen zu stabiler Performance in einer Woche

Latenz an Codepfade binden
Nennen Sie langsame Endpunkte und wir verfolgen sie zurück zu churnigen Tabellen und fehlerhafter App-Logik.

Ein Startup launcht einen Activity-Feed: Likes, Kommentare und "seen"-Marker. Über Nacht steigen die Writes von einigen Dutzend pro Sekunde auf mehrere Tausend. Die App funktioniert weiter, aber Seiten, die früher in 200 ms luden, brauchen jetzt 2–5 Sekunden.

Innerhalb von zwei Tagen fallen zwei Dinge auf: Indizes wachsen schnell, die Festplattennutzung steigt weiter, obwohl alte Zeilen gelöscht werden, und On-Call bekommt Low-Disk-Alerts. Die Abfragelatenz schwankt stark: kurz gut, dann plötzlich langsam.

Was wir fanden

Der Hotspot ist eine Tabelle, die ständig geändert wird, zum Beispiel activity_events. Sie hat häufige Updates (Statusflags) und Deletes (Retention-Cleanup). Autovacuum läuft, aber es kommt nie hinterher, weil die Tabelle schnell ihre Schwelle erreicht und während des Laufens weiter verändert wird. Tote Tupel häufen sich, Indizes blähen auf, und einfache Abfragen müssen viel mehr Pages lesen als nötig.

Was wir verändert haben (ohne riskante "Big Bang"-Wartung)

Wir lösten es mit kleinen, gezielten Schritten:

  • Pro-Tabellen-Autovacuum-Schwellen so gesetzt, dass diese hochfrequente Tabelle früher und öfter vacuumed.
  • Autovacuum-Kapazität für diese Tabelle erhöht (Cost-Limit oder mehr Worker), damit es vor dem nächsten Spike fertig wird.
  • Zielgerichtetes Reindex der schlimmsten Indizes in einem Niedriglast-Fenster durchgeführt.
  • Wöchentliches Wartungsfenster für die wenigen Tabellen geplant, die am meisten churnen.
  • Retention-Jobs so angepasst, dass sie in kleineren Batches löschen, damit Vacuum mithalten kann.

Am Ende der Woche flachte das Festplattenwachstum ab und blieb vorhersehbar. Die Abfragelatenz schwankte nicht mehr stark, und der Activity-Feed lief wieder stabil.

Nächste Schritte: Stabil halten und die Ursache beheben

Sobald Bloat unter Kontrolle ist, ändert sich das Ziel: vermeiden, dass es wiederkommt. Der sicherste Weg ist, jeweils eine Änderung vorzunehmen und zu beobachten. So vermeiden Sie, ein Problem gegen ein anderes einzutauschen.

Beginnen Sie mit dem schlimmsten Verursacher (eine Tabelle oder Queue-Tabelle mit vielen Updates) und machen Sie eine klare Änderung. Zum Beispiel: Schwellen und Scale-Factors für diese Tabelle senken, das Cost-Limit erhöhen, um Vacuum fertigzustellen, Churn in der App reduzieren, indem unnötige Neuschreibungen vermieden werden, oder Alerts für tote Tupel, Vacuum-Lag und lange Transaktionen hinzufügen.

Nach jeder Änderung verfolgen Sie eine Woche lang ein paar Kennzahlen: Tabellenwachstum, tote Tupel, Autovacuum-Frequenz und p95-Abfragezeit für Endpunkte, die diese Tabelle treffen. Wenn sich nichts verbessert, rollen Sie zurück und probieren die nächste Idee.

Schreiben Sie ein kleines Runbook, das Ihr Team wiederholen kann:

  • Welche Metrik Aktion auslöst (z. B. tote Tupel über X% oder Vacuum-Lag über Y Stunden)
  • Was Sie zuerst ändern (zuerst table-level Einstellungen, bevor globale Änderungen)
  • Wie Sie Erfolg verifizieren (zwei Metriken und eine nutzerrelevante Prüfung)
  • Was zu vermeiden ist (manuelle VACUUMs in Spitzenzeiten, lange Transaktionen)
  • Wer die Entscheidung trifft und wann eskaliert wird

Wenn Ihr Projekt KI-generiert ist, suchen Sie nach Code-Mustern, die konstanten Churn erzeugen: chatty Writes (Zeilen bei jeder Anfrage aktualisieren), fehlende Indizes auf Foreign Keys und schlechtes Session-Handling, das Transaktionen offenlässt. Ein typisches Beispiel ist ein Prototyp, der bei jedem Seitenaufruf last_seen schreibt.

Wenn Sie weiter tunen, aber die Performance driftet, ist es meist ein Produktverhaltensproblem, kein reines Vacuum-Problem. Falls Sie mit einem KI-erstellten Codebase kämpfen, die Postgres auf seltsame Weise quält (z. B. kaputte Auth-Flows, Retry-Loops, Queue-Jobs, die nie stoppen), kann FixMyMess unter fixmymess.ai helfen, diese Codepfade zu diagnostizieren und zu reparieren, damit die Datenbank nicht gegen unlösbare Probleme kämpft.