Что такое VACUUM в PostgreSQL?

SeniorPostgreSQL · Backend·Обновлено 28 июня 2026
Коротко
VACUUM — команда обслуживания PostgreSQL, которая удаляет «мёртвые» версии строк, накопившиеся из-за MVCC, и возвращает место на диске или в пул свободных страниц таблицы. Без регулярного запуска VACUUM база замедляется и в конечном счёте перестаёт работать из-за переполнения счётчика транзакций.

Зачем нужен VACUUM

PostgreSQL реализует изоляцию транзакций через MVCC (Multi-Version Concurrency Control): при UPDATE старая версия строки не перезаписывается, а помечается «мёртвой» (dead tuple), а рядом создаётся новая версия. При DELETE строка тоже лишь помечается мёртвой. Это позволяет параллельным транзакциям видеть согласованный снимок данных без блокировок чтения.

Со временем мёртвых версий накапливается всё больше — они занимают место в страницах таблицы и индексах, замедляют sequential scan и seq-сканы индексов. VACUUM — механизм, который убирает эти мёртвые версии.

Что делает VACUUM

  1. Помечает страницы с мёртвыми кортежами как пригодные для повторного использования (но не возвращает место ОС).
  2. Обновляет карту видимости (visibility map) — битовый файл, сигнализирующий планировщику, что страница содержит только живые кортежи, видимые всем транзакциям. Это ускоряет Index Only Scan.
  3. Обновляет карту свободного пространства (free space map, FSM) — чтобы INSERT знал, куда вставлять новые строки.
  4. Предотвращает Transaction ID Wraparound — одна из критических задач (см. ниже).

VACUUM vs VACUUM FULL

Режим Поведение Блокировка Когда использовать
VACUUM Помечает место свободным внутри файла таблицы ShareUpdateExclusiveLock (DDL заблокирован, DML — нет) Регулярное обслуживание
VACUUM FULL Перезаписывает таблицу целиком в новый файл AccessExclusiveLock (таблица полностью заблокирована) Разовое высвобождение места ОС после массового DELETE

VACUUM FULL — тяжёлая операция: она эквивалентна CREATE TABLE AS SELECT + переименование. На больших таблицах — часы простоя. Альтернатива — расширение pg_repack, которое делает то же самое без полной блокировки.

VACUUM ANALYZE

Команда VACUUM ANALYZE совмещает сборку мусора с обновлением статистики для планировщика запросов. Устаревшая статистика — частая причина плохих планов (nested loop вместо hash join и т.п.).

Autovacuum

PostgreSQL запускает VACUUM автоматически через демон autovacuum. Ключевые параметры настройки:

-- Посмотреть текущие параметры autovacuum
SHOW autovacuum_vacuum_threshold;       -- минимум мёртвых кортежей для запуска
SHOW autovacuum_vacuum_scale_factor;    -- доля от размера таблицы
SHOW autovacuum_vacuum_cost_delay;      -- пауза между порциями работы (throttle)

-- Переопределить параметры для конкретной таблицы (горячие таблицы)
ALTER TABLE orders SET (
  autovacuum_vacuum_scale_factor = 0.01,  -- чаще убираем мусор (1% вместо 20%)
  autovacuum_vacuum_cost_delay = 2        -- меньше throttle
);

Transaction ID Wraparound — критическая задача

PostgreSQL использует 32-битный счётчик транзакций (XID). После ~2 миллиардов транзакций он обнуляется. VACUUM записывает relfrozenxid — границу, до которой все XID «заморожены» и считаются видимыми всем. Если VACUUM не успевает, база переходит в аварийный режим read-only и отказывается принимать запись:

-- Контролировать близость к wraparound
SELECT relname,
       age(relfrozenxid) AS xid_age,
       2000000000 - age(relfrozenxid) AS xids_left
FROM pg_class
WHERE relkind = 'r'
ORDER BY xid_age DESC
LIMIT 10;
-- Тревога: xid_age > 1.5 млрд → срочно запускать VACUUM FREEZE

Мониторинг VACUUM

-- Статистика autovacuum по таблицам
SELECT relname,
       n_dead_tup,
       n_live_tup,
       last_autovacuum,
       last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC;

Что хочет услышать интервьюер

Понимание связи VACUUM с MVCC: кандидат должен объяснить, почему мёртвые кортежи вообще появляются и почему PostgreSQL не удаляет их сразу

Разницу между VACUUM и VACUUM FULL, в том числе по уровню блокировок и применимости на production

Роль autovacuum и умение настраивать его параметры под нагрузку конкретной таблицы

Проблему Transaction ID Wraparound и её последствия — это маркер senior-уровня: кандидат должен знать, что нерабочий autovacuum способен остановить всю базу

Инструменты мониторинга: pg_stat_user_tables, pg_class.relfrozenxid, логи autovacuum

Пример: Ручной запуск VACUUM и проверка bloat

-- Обычный VACUUM: убирает мусор, не блокирует DML
VACUUM orders;

-- VACUUM с обновлением статистики планировщика
VACUUM ANALYZE orders;

-- VACUUM FULL: перезаписывает таблицу, возвращает место ОС (блокирует таблицу!)
VACUUM FULL orders;

-- VACUUM FREEZE: принудительно замораживает XID (для контроля wraparound)
VACUUM FREEZE orders;

-- Посмотреть количество мёртвых кортежей и когда последний раз убирал autovacuum
SELECT
  relname                      AS таблица,
  n_live_tup                   AS живых_строк,
  n_dead_tup                   AS мёртвых_строк,
  round(n_dead_tup::numeric
    / NULLIF(n_live_tup + n_dead_tup, 0) * 100, 1) AS bloat_pct,
  last_autovacuum,
  last_autoanalyze
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;

Пример: Мониторинг Transaction ID Wraparound

-- Возраст XID каждой таблицы (критично, если > 1.5 млрд)
SELECT
  schemaname,
  relname,
  age(relfrozenxid)                          AS xid_age,
  2000000000 - age(relfrozenxid)             AS xids_until_danger
FROM pg_class
JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
WHERE relkind = 'r'
  AND schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY xid_age DESC
LIMIT 20;

-- Если xid_age > 1.5 млрд — немедленно:
-- VACUUM FREEZE VERBOSE <таблица>;

Пример: Тонкая настройка autovacuum для горячей таблицы

-- Для таблицы с очень частыми UPDATE/DELETE снижаем порог запуска autovacuum
ALTER TABLE user_sessions SET (
  -- Запускать VACUUM уже при 1% мёртвых кортежей (по умолчанию 20%)
  autovacuum_vacuum_scale_factor = 0.01,
  -- Минимум 100 мёртвых кортежей (по умолчанию 50)
  autovacuum_vacuum_threshold = 100,
  -- Уменьшить паузу между порциями — агрессивнее убираем мусор
  autovacuum_vacuum_cost_delay = 2,
  -- Аналогично для ANALYZE
  autovacuum_analyze_scale_factor = 0.005
);

-- Проверить, применились ли параметры
SELECT reloptions
FROM pg_class
WHERE relname = 'user_sessions';

Типичные ошибки

Путают VACUUM и VACUUM FULL: думают, что обычный VACUUM возвращает место операционной системе — нет, он только освобождает страницы для повторного использования внутри файла

Отключают или игнорируют autovacuum на «тихих» таблицах, не зная о проблеме XID Wraparound, что приводит к аварийной остановке базы

Запускают VACUUM FULL на production в рабочее время, блокируя всю таблицу на часы вместо использования pg_repack

Не мониторят n_dead_tup и last_autovacuum — обнаруживают bloat только когда диск уже переполнен или запросы деградировали

Не знают о VACUUM FREEZE и его роли — считают, что autovacuum с дефолтными настройками всегда справляется с заморозкой XID

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее