# Multixact и relminmxid _Vacuum, freeze, wraparound · PostgreSQL Knowledge Base_ **TL;DR:** Когда одну строку блокируют несколько транзакций сразу (FOR SHARE, внешние ключи), вместо одного xid в xmax пишется multixact - ссылка на список участников. Это второй 32-битный счётчик со своим кругом, переполнением и границей relminmxid, за которым следят отдельно. ## Зачем понадобился В `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](/courses/postgres/kb/freeze.md)). ## Почему его легко проглядеть Типовой мониторинг следит за `age(relfrozenxid)` и упускает `age(relminmxid)`. На нагрузке с обилием блокировок строк или внешних ключей multixact-счётчик может побежать к краю быстрее обычного. Поэтому следить нужно за обоими: ```sql 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](/courses/postgres/kb/wraparound.md): анти-wraparound autovacuum, затем отказ базы принимать команды. ## Команды ```sql SELECT relname, mxid_age(relminmxid) FROM pg_class ORDER BY 2 DESC LIMIT 5; ``` Возраст multixact-границы по таблицам — отдельно от relfrozenxid ```sql SELECT datname, mxid_age(datminmxid) FROM pg_database ORDER BY 2 DESC; ``` Возраст multixact на уровне баз ## См. также - [Wraparound и колесо XID](/courses/postgres/kb/wraparound.md) - [Заморозка и relfrozenxid](/courses/postgres/kb/freeze.md) - [VACUUM и removable cutoff](/courses/postgres/kb/vacuum.md)