PostgreSQL: database is not accepting commands to avoid wraparound data loss

ERROR:  database is not accepting commands to avoid wraparound data loss in database "appdb"
HINT:  Stop the postmaster and vacuum that database in single-user mode.

Dies ist die Notbremse von PostgreSQL MVCC — und falls Sie dies während des Notfalls lesen: die Lösung ist, alles zu entfernen, was das Vacuum blockiert, und dann VACUUM die ältesten Tabellen freezen zu lassen. Beachten Sie, dass der klassische HINT über den Single-User-Modus auf modernen Versionen weithin als veralteter Rat gilt; ein normales VACUUM erledigt die Aufgabe meist besser und sicherer.

Was dieser Fehler bedeutet

Transaktions-IDs (XIDs) sind 32-Bit-Werte, die aus einem Zähler zugewiesen werden, der sich im Kreis herumdreht. Damit die Zeilensichtbarkeit korrekt bleibt, darf keine lebende Zeile älter als etwa 2 Milliarden Transaktionen sein — daher freezt VACUUM kontinuierlich alte Zeilen, markiert sie als für-immer-sichtbar und gewinnt ihr Alter zurück. Wenn das Freezing weit genug zurückfällt, warnt PostgreSQL zuerst (database "appdb" must be vacuumed within N transactions) und hört dann ganz auf, datenmodifizierende Befehle anzunehmen, ein Sicherheitspuffer, bevor ein tatsächlicher Wraparound die Sichtbarkeit beschädigen würde.

Die entscheidende Erkenntnis: dies ist praktisch nie allein "das Vacuum war zu langsam" — etwas hat den Freeze-Horizont zurückgehalten. Finden Sie es zuerst; ein VACUUM, das gegen einen unbeweglichen Horizont anrennt, erreicht nichts.

Häufige Ursachen

So diagnostizieren Sie ihn

-- Wie alt die Datenbank / die schlimmsten Tabellen sind:
SELECT datname, age(datfrozenxid) FROM pg_database ORDER BY 2 DESC;
SELECT relname, age(relfrozenxid) FROM pg_class
WHERE relkind = 'r' ORDER BY 2 DESC LIMIT 20;

-- Die drei üblichen Schuldigen des blockierten Horizonts:
SELECT pid, now() - xact_start AS age, state, query
FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY xact_start LIMIT 5;

SELECT * FROM pg_prepared_xacts;

SELECT slot_name, active, xmin FROM pg_replication_slots;

So beheben Sie ihn

  1. Entfernen Sie die Horizont-Blockierer: beenden Sie die uralten Transaktionen, COMMIT PREPARED / ROLLBACK PREPARED die verwaisten Two-Phase-Transaktionen, droppen Sie tote Replikations-Slots.
  2. Vacuumen Sie zuerst die schlimmsten Übeltäter: führen Sie VACUUM (VERBOSE) auf den Tabellen mit dem höchsten relfrozenxid-Alter aus (oder datenbankweit, wenn die Zeit reicht). Mit entfernten Blockierern sinkt das Alter und die Datenbank nimmt wieder Schreibvorgänge an, sobald sie wieder unter der Sicherheitsschwelle ist.
  3. Danach: machen Sie es strukturell: überwachen Sie age(datfrozenxid) mit Alarmen weit unter der 2-Milliarden-Obergrenze, halten Sie Autovacuum an und angemessen ausgestattet, und alarmieren Sie bei Prepared Transactions und inaktiven Slots — den stillen Horizont-Haltern.
🔍 Wesentlicher Hintergrund: VACUUM, Autovacuum und Bloat — der Freezing-Mechanismus und wie Autovacuum sich selbst plant.