Почему база данных выросла после VACUUM FULL и как исправить

    Ситуация, когда после выполнения VACUUM FULL ANALYZE размер базы данных увеличивается вместо уменьшения, встречается на практике. В одном из проектов объём БД вырос с 1,4 ТБ до 2 ТБ. При этом дамп и восстановление на другом сервере возвращали нормальный размер 1,4 ТБ. Разберёмся, что произошло и как найти «потерянное» место.

    Почему VACUUM FULL увеличивает базу данных

    Операция VACUUM FULL перестраивает таблицы и индексы, создавая новые файлы данных. В процессе: старые файлы не удаляются сразу, пока не завершится перезапись; создаются временные копии .tmp; при снятии снимка виртуальной машины файловая система фиксирует состояние «до завершения». Если в этот момент сделать снапшот, новые файлы остаются, а старые - не удаляются, так как ссылки на них ещё активны.

    Основные причины роста на 30-40%

    • Снапшот во время VACUUM FULL - виртуальная машина «заморозила» процесс, и файлы не были очищены.
    • Временные файлы и WAL-журналы - хотя вы исключили временные файлы, стоит проверить pg_wal и архивы.
    • «Мёртвые» строки и TOAST-таблицы - после сбоя часть старых версий строк могла остаться в системных таблицах.

    Как определить, что именно занимает место

    Чтобы найти «лишние» гигабайты, выполните диагностику на уровне файловой системы и внутри PostgreSQL.

    1. Проверьте каталог данных

    Зайдите на сервер и выполните:

    du -sh /var/lib/postgresql/*/base/*/

    Сравните размеры баз данных с ожидаемыми. Если какая-то база весит больше, чем сумма её таблиц - проблема в файлах, не привязанных к объектам.

    2. Исключите WAL и архивы

    Проверьте размер pg_wal:

    du -sh /var/lib/postgresql/*/pg_wal/

    Если он превышает несколько гигабайт, настройте wal_keep_segments или архивацию.

    3. Найдите потерянные файлы

    В каталоге base/ могут лежать файлы, не соответствующие ни одной таблице. Используйте запрос:

    SELECT oid, relname, relfilenode FROM pg_class WHERE relfilenode > 0;

    Сравните список relfilenode с файлами в base/<db_oid>/. Файлы, которых нет в выводе - «осиротевшие».

    4. Проверьте TOAST-таблицы

    TOAST может хранить большие объёмы данных. Запрос:

    SELECT relname, pg_size_pretty(pg_total_relation_size(oid)) FROM pg_class WHERE reltoastrelid > 0;

    Если TOAST-таблица весит неожиданно много - выполните VACUUM FULL для неё отдельно.

    Как вернуть базу к нормальному размеру

    Если вы нашли «осиротевшие» файлы, самый надёжный способ - сделать pg_dump и восстановить на новом сервере. Это гарантированно уберёт весь мусор. Альтернатива - удалить лишние файлы вручную (только если уверены, что они не нужны).

    Что делать, чтобы избежать повторения

    • Не делайте снапшоты ВМ во время VACUUM FULL или других операций, переписывающих файлы.
    • Используйте VACUUM (без FULL) для регулярной очистки.
    • Настройте мониторинг размера каталога base/ и pg_wal.

    В описанном кейсе проблема решилась дампом и восстановлением - размер вернулся к 1,4 ТБ. После этого настроили автоматический VACUUM и запретили снапшоты во время обслуживания.

    Часто задаваемые вопросы