Зачем понадобился
В xmax строки обычно пишется один xid - транзакция, удалившая или
заблокировавшая версию. Но строку могут заблокировать несколько
транзакций одновременно: SELECT ... FOR SHARE, проверки внешних ключей,
совместные блокировки. Один xid тут не подходит - блокировщиков много.
Тогда PostgreSQL заводит multixact: отдельный идентификатор, за которым
стоит список транзакций-участников и их режимы блокировки. В xmax
строки оказывается этот multixact-id, а не обычный xid.
Второй счётчик, второй wraparound
Multixact-идентификаторы - тоже 32-битный счётчик, со своим кругом и своим переполнением. У него своя инфраструктура:
relminmxid(вpg_class) - граница заморозки multixact таблицы, аналогrelfrozenxid;autovacuum_multixact_freeze_max_age(400 млн) - возраст, при котором запускается анти-wraparound проход по multixact;- данные хранятся в SLRU-каталогах
pg_multixact/.
VACUUM морозит multixact так же, как обычные версии (см. freeze).
Почему его легко проглядеть
Типовой мониторинг следит за age(relfrozenxid) и упускает
age(relminmxid). На нагрузке с обилием блокировок строк или внешних
ключей multixact-счётчик может побежать к краю быстрее обычного. Поэтому
следить нужно за обоими:
SELECT relname,
age(relfrozenxid) AS xid_age,
mxid_age(relminmxid) AS mxid_age
FROM pg_class WHERE relkind = 'r'
ORDER BY mxid_age(relminmxid) DESC LIMIT 10;
Симптомы переполнения и лестница защиты те же, что у обычного wraparound: анти-wraparound autovacuum, затем отказ базы принимать команды.