У версионной базы есть неприятная цена: чтобы понять, видна ли строка,
обычно нужно прочитать её заголовок и проверить транзакционные поля. Карта
видимости - форк _vm (см. relfilenode-forks) - даёт срезку для целых
страниц: если на странице заведомо всё видимо, проверять каждую строку не
нужно.
Два бита на страницу
| Бит | Смысл |
|---|---|
| all-visible | каждая строка на странице видна всем транзакциям |
| all-frozen | каждая строка на странице заморожена |
Бит all-visible означает: все версии на странице зафиксированы, давно видимы и нет мёртвых, ждущих уборки. Бит all-frozen - более сильное утверждение: версии не просто видимы, а заморожены, и их возраст уже не важен.
Посмотреть биты по страницам:
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%.