Переполнение счетчика транзакций в PostgreSQL: Траблшутинг TXID Wraparound
- Предупреждения в логах
WARNING: database "mydb" must be vacuumed within 10000000 transactions. - Аварийный переход СУБД в режим Read-Only с ошибкой
FATAL: database is not accepting commands to avoid wraparound data loss in database. - Зависание автовакуума на таблицах с большим количеством мертвых строк.
1. Мониторинг возраста транзакций (XID Age)
Определите, какие базы данных и таблицы ближе всего подошли к лимиту переполнения счетчика (2 млрд транзакций):
-- Проверка возраста по базам данных
SELECT
datname,
age(datfrozenxid) AS age_in_xids,
2147483648 - age(datfrozenxid) AS xids_until_emergency_stop
FROM pg_database
ORDER BY age(datfrozenxid) DESC;Поиск конкретных таблиц с критическим relfrozenxid:
SELECT
c.oid::regclass AS table_name,
age(c.relfrozenxid) AS xid_age,
pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 't', 'm')
AND n.nspname NOT IN ('pg_toast', 'pg_catalog', 'information_schema')
ORDER BY age(c.relfrozenxid) DESC
LIMIT 15;2. Тюнинг параметров агрессивной заморозки в postgresql.conf
# Порог запуска форсированного вакуума для защиты от wraparound
autovacuum_freeze_max_age = 1500000000
# Настройки фонового автовакуума
autovacuum_max_workers = 6
autovacuum_vacuum_cost_limit = 2000
autovacuum_vacuum_cost_delay = 23. Экстренная ручная заморозка зависшей таблицы
Если база данных близка к блокировке, запустите ручной VACUUM FREEZE в несколько потоков:
-- Запуск полной заморозки с максимальной скоростью
VACUUM (FREEZE, VERBOSE, ANALYZE) problematic_table_name;4. Восстановление при аварийной остановке (Emergency Mode)
Если СУБД остановилась и выдает FATAL, запустите PostgreSQL в однопользовательском режиме (single-user mode):
# Остановка демона
systemctl stop postgresql
# Запуск однопользовательского режима от имени postgres
sudo -u postgres postgres --single -D /var/lib/postgresql/16/main mydb
# В консоли введите команду:
VACUUM FREEZE;
# Выход по нажатию Ctrl+D Частые вопросы (FAQ)
Почему в PostgreSQL существует проблема 2 миллиардов транзакций?
Счетчик транзакций (XID) является 32-битным числом с модульной арифметикой. Каждая транзакция видит до 2^31 транзакций в 'прошлом' и 2^31 в 'будущем'. Если возраст транзакций превышает 2 млрд без заморозки (FREEZE), старые данные станут невидимыми (уйдут в 'будущее').
Что делает флаг FREEZE во время выполнения VACUUM?
Он заменяет старые XID в заголовках строк (t_xmin) на специальный замороженный идентификатор FrozenTransactionId (XID = 2), после чего строка считается бесконечно старой и видимой для всех будущих транзакций.
Может ли параметр autovacuum = off защитить от Wraparound?
Нет. Даже если autovacuum выключен глобально, при достижении autovacuum_freeze_max_age ядро PostgreSQL принудительно запустит autovacuum-воркеры вопреки запрету.
Как долго выполняется VACUUM FREEZE на таблице объемом 1 ТБ?
Время зависит от производительности дисковой подсистемы и настроек vacuum_cost_limit. При отключенном троттлинге (vacuum_cost_delay = 0) на современных NVMe скорость ограничивается чтением и записью страниц (обычно от 30 минут до нескольких часов).