PostgreSQL nimmt keine Befehle mehr an: Wraparound
Die Datenbank verweigert jede schreibende Transaktion. Nicht die Platte ist voll, sondern die Transaktionsnummern gehen aus — Autovacuum kam nicht durch.
Aufwand:
ca. 45 Min.
Voraussetzung:
Datenbankzugang mit Superuser-Rechten, psql
Gilt für:
PostgreSQL 18
Das Symptom
Die Anwendung kann nichts mehr schreiben. Jeder Schreibversuch endet mit einem
Fehler, wonach die Datenbank keine Befehle mehr annimmt, die neue
Transaktionsnummern vergeben — um Datenverlust durch Wraparound zu vermeiden. Als
Hinweis steht dabei, in dieser Datenbank ein datenbankweites VACUUM auszuführen.
Lesen geht meist noch. Die Platte ist nicht voll, der Arbeitsspeicher reicht, die
Verbindungen stehen. Im Serverprotokoll finden sich rückblickend Wochen alte
Warnungen, dass die Datenbank innerhalb einer bestimmten Anzahl von Transaktionen
aufgeräumt werden müsse. Gelesen hat sie niemand.
Die „Layer 8“-Wahrheit
PostgreSQL nummeriert Transaktionen, und dieser Zähler ist endlich. Damit er
umlaufen kann, ohne dass alte Daten plötzlich in der Zukunft zu liegen scheinen,
müssen alte Zeilenversionen rechtzeitig eingefroren werden. Genau dafür gibt es
Autovacuum. Es läuft von allein, es läuft ständig, und es meldet sich, wenn es nicht
hinterherkommt. Der Fehler ist also nicht, dass die Datenbank stehen bleibt — der
Fehler ist, dass sie es angekündigt hat und niemand hingesehen hat.
Warum Autovacuum nicht durchkommt, ist fast immer dieselbe kurze Liste. Eine
Sitzung, die vor drei Wochen ein BEGIN abgesetzt und nie beendet hat. Ein
Replikations-Slot für einen Standby-Server, den es seit dem letzten Umbau nicht
mehr gibt. Beide tun dasselbe: Sie beanspruchen eine alte Sicht auf die Daten, und
solange die besteht, darf Vacuum die betreffenden Zeilen nicht einfrieren. Der
Zähler läuft trotzdem weiter.
Der zweite verbreitete Fehler ist die Reparaturanleitung. Ein großer Teil der
Ratschläge im Netz schickt einen in den Einzelbenutzermodus, weil das vor vielen
Jahren tatsächlich nötig war. Das Handbuch widerspricht heute ausdrücklich: In
typischen Fällen ist das nicht mehr erforderlich und sollte vermieden werden, weil es
das System außer Betrieb nimmt. Wer also im Ernstfall zuerst den Server herunterfährt,
verlängert den Ausfall aus Traditionsgründen.
Die echte, verifizierte Lösung
1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:
1.
Feststellen, wie knapp es wirklich ist
Zuerst den Abstand jeder Datenbank zur ältesten eingefrorenen Transaktionsnummer messen. Das Handbuch nennt genau diese Abfrage als bequemen Weg, die Information einzusehen. Der zurückgegebene Wert ist ein Alter in Transaktionen — je größer, desto dringender. Er gehört in die Überwachung, nicht in eine einmalige Notfallsitzung.
SELECT datname, age(datfrozenxid) FROM pg_database;
2.
Die Bremsklötze finden und lösen
Ein Vacuum, das nicht einfrieren darf, läuft ins Leere. Das Handbuch nennt zwei Hauptverdächtige. Erstens lang laufende offene Transaktionen: in pg_stat_activity nach Zeilen mit großem age(backend_xid) oder age(backend_xmin) suchen und diese Sitzungen beenden. Zweitens alte Replikations-Slots: in pg_replication_slots nach Slots mit großem age(xmin) oder age(catalog_xmin) suchen und nicht mehr benötigte entfernen. Erst danach lohnt das Aufräumen.
SELECT pid, age(backend_xid), age(backend_xmin), state, query
FROM pg_stat_activity
ORDER BY age(backend_xmin) DESC NULLS LAST;
SELECT slot_name, active, age(xmin), age(catalog_xmin)
FROM pg_replication_slots;
3.
Datenbankweit aufräumen
Der im Fehlerhinweis geforderte Schritt: ein datenbankweites VACUUM in der betroffenen Datenbank. Das dauert bei großen Beständen lange und darf nicht abgebrochen werden, weil sonst wieder von vorn begonnen wird. FREEZE erzwingt dabei ein aggressives Einfrieren, statt nur die ohnehin fälligen Bereiche anzufassen.
vacuumdb --all --freeze --verbose
4.
Nicht mehr benötigte Tabellen loswerden statt aufräumen
Wenn große Tabellen ohnehin Müll enthalten, ist es schneller, sie zu entfernen, als sie einzufrieren. Genau das nennt das Handbuch als einzigen verbliebenen Grund, überhaupt in den Einzelbenutzermodus zu gehen: um nicht benötigte Tabellen mit TRUNCATE oder DROP zu beseitigen, damit man sie gar nicht erst aufräumen muss. Im Normalbetrieb geht das genauso — nur mit weniger Drama.
TRUNCATE TABLE protokoll_alt;
DROP TABLE import_zwischenstand;
5.
Dafür sorgen, dass es nicht wieder passiert
Der Grenzwert, ab dem Autovacuum ein Einfrieren erzwingt, ist autovacuum_freeze_max_age. Das Handbuch beziffert die Vorgabe auf 200 Millionen Transaktionen und rechnet vor, dass das etwa 50 MB Speicher für pg_xact und etwa 2 GB für pg_commit_ts bedeutet. Wer diesen Wert hochsetzt, um Ruhe zu haben, verschiebt nur den Termin der nächsten Notbremse. Sinnvoller ist es, das Alter zu überwachen und Autovacuum nicht auszubremsen — also weder ausschalten noch mit Dauertransaktionen und vergessenen Slots blockieren.
SHOW autovacuum_freeze_max_age;
Häufige Fragen (FAQ)
?Was bedeutet die Meldung, dass die Datenbank keine Befehle mehr annimmt, die neue Transaktionsnummern vergeben?
Sie bedeutet, dass PostgreSQL sich selbst angehalten hat, um Datenverlust zu vermeiden. Das Handbuch nennt genau diesen Fehlertext und gibt als Hinweis dazu, in der betroffenen Datenbank ein datenbankweites VACUUM auszuführen. Vorher wurde bereits gewarnt: Die Datenbank müsse innerhalb einer bestimmten Anzahl von Transaktionen aufgeräumt werden. Es handelt sich also nicht um einen Absturz und nicht um eine defekte Platte, sondern um eine Notbremse, die nach einer langen Reihe ignorierter Warnungen greift.
?Muss ich den Server dafür in den Einzelbenutzermodus herunterfahren?
In aller Regel nein — auch wenn genau das in vielen älteren Anleitungen steht. Das Handbuch sagt heute ausdrücklich, dass es in früheren Versionen manchmal nötig war, den Postmaster anzuhalten und die Datenbank im Einzelbenutzermodus aufzuräumen, dass dies in typischen Fällen aber nicht mehr erforderlich ist und vermieden werden sollte, weil es das System außer Betrieb nimmt. Der einzige genannte Grund für den Einzelbenutzermodus ist, nicht mehr benötigte Tabellen mit TRUNCATE oder DROP loszuwerden, damit man sie gar nicht erst aufräumen muss.
?Warum ist Autovacuum nicht von selbst durchgekommen?
Weil etwas alte Zeilenversionen festhält. Das Handbuch nennt als Prüfschritte, lang laufende offene Transaktionen zu beenden — auffindbar in pg_stat_activity über Zeilen mit großem age(backend_xid) oder age(backend_xmin) — und alte Replikations-Slots zu entfernen, auffindbar in pg_replication_slots über Slots mit großem age(xmin) oder age(catalog_xmin). Solange irgendjemand oder irgendetwas eine alte Sicht auf die Daten beansprucht, darf Vacuum die betreffenden Zeilen nicht einfrieren, und der Abstand wächst weiter.
Belege
Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.
Die Sortierbibliothek des Betriebssystems hat sich geändert. PostgreSQL warnt vor einem Versions-Konflikt — Indizes auf Textspalten sind dann unbrauchbar.