Откуда берётся
Каждой изменяющей транзакции PostgreSQL выдаёт xid - следующее значение
32-битного счётчика. Видимость версии решается сравнением xmin/xmax со
снимком: меньше - прошлое, больше - будущее. Но 32 бита вмещают лишь ~4
млрд значений, и на активной базе счётчик переполняется за недели-месяцы.
Круг вместо прямой
Чтобы сравнения работали после обнуления, PostgreSQL считает их на круге:
для любого xid ~2 млрд значений назад - прошлое, ~2 млрд вперёд -
будущее. Ловушка: версия, чей xmin отстал больше чем на 2 млрд,
«перепрыгивает» в будущее и становится невидимой. Данные физически на
месте, но СУБД считает их несуществующими - это и есть wraparound,
молчаливая потеря данных.
Защита: заморозка
Не дать ни одной версии состариться больше чем на 2 млрд - задача заморозки (см. freeze). VACUUM морозит старые версии, выводя их из круга. Пока заморозка поспевает, wraparound невозможен.
Лестница защиты
| Возраст | Что включается |
|---|---|
≥ 200 млн (autovacuum_freeze_max_age) | анти-wraparound autovacuum, даже при выключенном autovacuum |
≥ 1.6 млрд (vacuum_failsafe_age) | failsafe: VACUUM бросает очистку индексов и паузы, гонит заморозку |
| < 40 млн до края | WARNING в лог с обратным отсчётом |
| < 3 млн до края | сервер отказывается выдавать xid, база встаёт |
Из остановившейся базы выходят через VACUUM в однопользовательском режиме:
postgres --single -D /var/lib/postgresql/data mydb
backend> VACUUM;
Как не попасть
До single-user доходят почти всегда те, кто отключил autovacuum «ради
производительности». Анти-wraparound autovacuum - первая и обычно
достаточная линия обороны (см. autovacuum). Следить за приближением
нужно по age(relfrozenxid) и не забывать про второй счётчик -
multixact.