# Visibility Map (карта видимости) _Хранение и формат на диске · PostgreSQL Knowledge Base_ **TL;DR:** Карта видимости (_vm) держит по два бита на страницу: all-visible (все строки видны всем) и all-frozen (все строки заморожены). Первый бит позволяет index-only scan не ходить в таблицу за проверкой видимости, а вакууму - пропускать страницу. Биты ставит VACUUM и сбрасывает любая запись в страницу. У версионной базы есть неприятная цена: чтобы понять, видна ли строка, обычно нужно прочитать её заголовок и проверить транзакционные поля. Карта видимости - форк `_vm` (см. [relfilenode-forks](/courses/postgres/kb/relfilenode-forks.md)) - даёт срезку для целых страниц: если на странице заведомо всё видимо, проверять каждую строку не нужно. ## Два бита на страницу | Бит | Смысл | |---|---| | all-visible | каждая строка на странице видна всем транзакциям | | all-frozen | каждая строка на странице заморожена | Бит **all-visible** означает: все версии на странице зафиксированы, давно видимы и нет мёртвых, ждущих уборки. Бит **all-frozen** - более сильное утверждение: версии не просто видимы, а заморожены, и их возраст уже не важен. Посмотреть биты по страницам: ```sql CREATE EXTENSION IF NOT EXISTS pg_visibility; VACUUM (FREEZE) flights; SELECT blkno, all_visible, all_frozen FROM pg_visibility('flights') ORDER BY blkno LIMIT 3; -- blkno | all_visible | all_frozen -- -------+-------------+------------ -- 0 | t | t ``` ## Зачем бит all-visible: index-only scan Главный выигрыш - **index-only scan**. Индекс хранит значения колонок, но не хранит, видима ли строка конкретной транзакции. Поэтому обычно после поиска по индексу приходится сходить в таблицу и проверить видимость строки. Но если страница помечена all-visible, проверять нечего: всё на ней видно всем. Тогда сервер берёт данные прямо из индекса и в таблицу не идёт. Поэтому свежий `VACUUM` часто превращает обычный indexed-доступ в более быстрый index-only. ## Зачем бит all-frozen: пропуск при вакууме Заморозка и борьба с переполнением счётчика транзакций требуют иногда проходить таблицу целиком. Бит all-frozen позволяет такому проходу пропускать страницы, где всё уже заморожено, - а это почти вся append-only таблица. Без карты каждый агрессивный вакуум перечитывал бы всю историю заново. ## Когда биты сбрасываются Любая запись в страницу - `INSERT`, `UPDATE`, `DELETE` - сбрасывает оба бита: теперь на странице есть свежая версия, чью видимость надо проверять. Поставить биты обратно может только `VACUUM`. Поэтому на активно изменяемой таблице доля all-visible страниц всё время скачет, а на редко меняемой держится у 100%. ## Команды ```sql CREATE EXTENSION IF NOT EXISTS pg_visibility; ``` Подключить функции чтения карты видимости ```sql SELECT blkno, all_visible, all_frozen FROM pg_visibility('flights'); ``` Биты видимости и заморозки по каждой странице таблицы ```sql SELECT * FROM pg_visibility_map_summary('flights'); ``` Сводка: сколько страниц all-visible и all-frozen ```sql SELECT pg_relation_size('flights', 'vm'); ``` Размер форка _vm в байтах ## См. также - [relfilenode и форки отношения](/courses/postgres/kb/relfilenode-forks.md) - [Free Space Map (карта свободного места)](/courses/postgres/kb/free-space-map.md) - [Раскладка страницы 8 КБ](/courses/postgres/kb/page-layout.md) - [Заголовок кортежа](/courses/postgres/kb/tuple-header.md)