DBAtlas · Атлас эксплуатации баз данных

Postgres XID wraparound / кризис autovacuum

Симптом

'database is not accepting commands to avoid wraparound data loss'; autovacuum не успевает, datfrozenxid стареет.

1. Оцените срочность

Идентификаторы транзакций PostgreSQL 32-битные; по мере их исчерпания сервер ОСТАНАВЛИВАЕТ ЗАПИСЬ, чтобы предотвратить потерю данных. Это критическая ситуация, ограниченная по времени. Хорошая новость: она решаема; нужна дисциплина, а не паника.

2. Измерьте, насколько близок предел

Получите возраст самого старого незамороженного XID для каждой базы данных; близость к autovacuum_freeze_max_age (по умолчанию 200M) показывает степень опасности.

PostgreSQL

SELECT datname, age(datfrozenxid) AS xid_age
FROM pg_database ORDER BY xid_age DESC;

3. Найдите самые старые таблицы

Проблема обычно в нескольких больших/часто изменяемых таблицах; сосредоточьтесь на самых старых значениях relfrozenxid.

PostgreSQL

SELECT relname, age(relfrozenxid) AS xid_age
FROM pg_class WHERE relkind IN ('r','m','t')
ORDER BY xid_age DESC LIMIT 20;

4. Устраните то, что блокирует VACUUM

Длительные открытые транзакции, заброшенные слоты репликации и prepared transactions блокируют очистку (freeze). Найдите и устраните их — иначе VACUUM не продвинется, сколько бы он ни работал.

PostgreSQL

SELECT pid, now()-xact_start AS age, state FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY age DESC;
SELECT slot_name, active FROM pg_replication_slots WHERE NOT active;
SELECT * FROM pg_prepared_xacts;

5. Выполните агрессивный FREEZE

После устранения блокирующих факторов принудительно выполните заморозку, начиная с самых старых таблиц. Даже если сервер перестал выдавать новые идентификаторы транзакций, не переходите в однопользовательский режим (postgres --single): в актуальной документации PostgreSQL сказано, что он больше не нужен и его следует избегать. Достаточно подключиться суперпользователем в обычном режиме и выполнить VACUUM; в PG14+ при приближении wraparound срабатывает механизм failsafe у VACUUM.

PostgreSQL

VACUUM (FREEZE, VERBOSE) oldest_table;
-- Entire database: vacuumdb --all --freeze --jobs=4

6. Навсегда: усильте и отслеживайте autovacuum

Увеличьте autovacuum_vacuum_cost_limit, уменьшите autovacuum_naptime и задайте параметры хранения на уровне таблиц для интенсивно изменяемых таблиц. Настройте оповещение по age(datfrozenxid); больше никогда не попадайте в этот кризис.

Открыть в DBAtlas →