Зачем
Идентификаторы транзакций - 32-битные и идут по кругу (см. wraparound).
Версия, чей xmin отстал больше чем на ~2 млрд, «переворачивается» в
будущее и становится невидимой. Чтобы этого не случилось, старые версии
замораживают: помечают как видимые всем и навсегда выводят из сравнений.
Как устроена
Исторически заморозка переписывала xmin в FrozenTransactionId (число 2).
Современный PostgreSQL не трогает xmin, а выставляет в заголовке кортежа
бит-подсказку HEAP_XMIN_FROZEN. Версия считается замороженной, исходный
xid остаётся виден для отладки. Делает заморозку vacuum.
relfrozenxid и age()
У каждой таблицы есть relfrozenxid в pg_class - граница, ниже которой
все версии заморожены. Полезнее смотреть её возраст:
SELECT relname, age(relfrozenxid) AS xid_age
FROM pg_class WHERE relkind = 'r'
ORDER BY age(relfrozenxid) DESC LIMIT 10;
age(relfrozenxid) растёт с каждой транзакцией и падает после прохода
VACUUM. На уровне базы то же показывает datfrozenxid в pg_database -
минимум по всем таблицам.
Пороги заморозки
vacuum_freeze_min_age(50 млн) - возраст версии, начиная с которого обычный VACUUM её морозит;autovacuum_freeze_max_age(200 млн) - возраст таблицы, при котором запускается агрессивный анти-wraparound проход, даже если autovacuum выключен;vacuum_freeze_table_age- возраст, при котором обычный VACUUM переходит в агрессивный режим (сканирует все страницы, а не только помеченные в карте видимости).
Агрессивный проход отличается тем, что не доверяет карте видимости и проходит даже «всё видно всем» страницы - иначе заморозить версии в них было бы нельзя.
Связь с multixact
Параллельно с relfrozenxid существует relminmxid - граница заморозки
multixact-идентификаторов (см. multixact). VACUUM продвигает обе.