PostgreSQL

VACUUM: сборка мусора и autovacuum

Таблица orders занимает 200 GB, но в ней всего 50 GB реальных данных. 150 GB - мусор от миллионов UPDATE за последние месяцы. Запросы медленные, диск полный. VACUUM FULL решит проблему за 2 часа простоя. pg_repack решит за те же 2 часа без остановки сервиса.

  • **Slack** (2019) обнаружил что таблица channels достигла 250 GB bloat из-за отставания autovacuum на волне роста COVID - пришлось экстренно запускать pg_repack в production; теперь dead_pct > 15% - триггер автоматического алерта
  • **Shopify** настраивает autovacuum_vacuum_scale_factor = 0.01 для таблицы orders (10M+ строк) - при дефолте 0.2 autovacuum запускался бы только при 2M dead tuples, слишком поздно
  • **Gitlab** мониторит txid wraparound риск через `age(datfrozenxid)` в pg_database: при приближении к 1.5B делают принудительный VACUUM FREEZE чтобы не допустить аварийного freeze всего кластера

Dead tuples - откуда берётся мусор

Каждый UPDATE в PG создаёт новую версию строки и помечает старую как мёртвую (dead tuple). DELETE тоже оставляет dead tuple - физически строка остаётся на месте, только xmax выставляется в txid удаляющей транзакции. Накопление dead tuples вызывает table bloat: файл таблицы растёт, seq scan медленнее, shared_buffers забивается мусором.

Долгоживущая транзакция (например зависший аналитический запрос или открытый курсор) удерживает xmin snapshot. VACUUM не может удалить dead tuples с xmax меньше этого xmin. Одна часовая транзакция способна накопить гигабайты мусора на активных таблицах.

Таблица имеет 1M live строк и 500K dead tuples. Почему Seq Scan медленнее чем на таблице с только 1M строк без dead tuples?

VACUUM vs VACUUM FULL - два разных инструмента

Обычный VACUUM помечает dead tuples как доступные для переиспользования, но не возвращает место OS. Физический размер файла не уменьшается. VACUUM FULL переписывает таблицу полностью в новый файл - возвращает место, но требует эксклюзивную блокировку (AccessExclusiveLock) на всё время работы. На таблице в 100 GB это может занять часы.

СвойствоVACUUMVACUUM FULL
БлокировкаShareUpdateExclusiveLock (слабая)AccessExclusiveLock (эксклюзивная)
Читатели/писателиПараллельная работа разрешенаВсе ждут
Возврат места OSНет (только reuse)Да
СкоростьБыстро (инкрементально)Медленно (полная перезапись)
ПроизводствоРегулярноТолько при крайней необходимости

Таблица занимает 50 GB, 80% - bloat. Запустили VACUUM. Сколько места вернётся OS?

Autovacuum tuning - правильные пороги

Autovacuum запускается когда `n_dead_tup > autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor * n_live_tup`. По умолчанию: threshold=50, scale_factor=0.2. Для таблицы в 100M строк это 20M dead tuples - запуск слишком редко. На горячих таблицах нужно понижать scale_factor до 0.01 или даже 0.001.

autovacuum_vacuum_cost_delay контролирует throttling: vacuum засыпает на delay мс после накопления cost_limit очков работы (чтение страницы = 1 очко, запись = 20). Это позволяет vacuum работать без убийства I/O. На SSD-серверах можно снизить до 0-2 мс.

Таблица events имеет 10M live строк. С дефолтным scale_factor=0.2 autovacuum запустится при скольких dead tuples?

Table bloat - диагностика и измерение

Bloat - разница между физическим размером таблицы и фактическим объёмом данных. Возникает когда dead tuples накапливаются быстрее чем VACUUM их убирает, или когда VACUUM убрал мусор но место не вернулось OS. Bloat в индексах (index bloat) критичен - раздутый индекс медленнее читается и не влезает в cache.

Bloat в индексах: `SELECT * FROM pgstatindex('idx_orders_user_id')` показывает leaf_fragmentation и avg_leaf_density. Density < 50% говорит о сильном index bloat. REINDEX CONCURRENTLY перестраивает без блокировки (PG 12+).

pgstattuple показал dead_tuple_percent=5%, но pg_relation_size вдвое больше ожидаемого. Как это возможно?

pg_repack - дефрагментация без блокировок

pg_repack перестраивает таблицу в новый файл без эксклюзивной блокировки: создаёт копию, применяет изменения через trigger, затем атомарно переключает. Вся операция занимает AccessExclusiveLock только на финальной перестановке - миллисекунды. Это замена VACUUM FULL для продакшена.

pg_repack создаёт trigger на исходной таблице для захвата изменений во время копирования. Это значит таблица должна иметь PRIMARY KEY (без него pg_repack не работает). Во время работы немного увеличивается нагрузка на запись из-за trigger overhead.

VACUUM FULL - стандартный способ бороться с bloat в продакшене

VACUUM FULL блокирует таблицу полностью на часы - недопустимо для продакшена. Для борьбы с bloat без простоя используется pg_repack. VACUUM FULL допустим только при плановых окнах обслуживания.

VACUUM FULL держит AccessExclusiveLock: никакие SELECT, INSERT, UPDATE, DELETE не проходят. На таблице orders в 50 GB это 30-60 минут полного downtime для всего что работает с этой таблицей.

Зачем pg_repack требует PRIMARY KEY на таблице?

Ключевые идеи

  • Dead tuples = физически живые строки с выставленным xmax; VACUUM помечает их как reusable но не возвращает место OS
  • Autovacuum threshold = 50 + scale_factor * live_rows; для больших таблиц scale_factor нужно снижать до 0.01 per-table
  • VACUUM FULL = полная блокировка на часы; pg_repack = перестройка без простоя через trigger-captured-changes

Связанные темы

VACUUM - часть экосистемы MVCC и мониторинга PG:

  • MVCC и dead tuples — Dead tuples - прямое следствие MVCC: UPDATE не перезаписывает, а добавляет; без VACUUM heap растёт бесконечно
  • Транзакции и долгие snapshot — Долгоживущие транзакции в REPEATABLE READ удерживают xmin - VACUUM не может удалить dead tuples старше этого xmin
  • Индексы — Index bloat накапливается так же как table bloat; REINDEX CONCURRENTLY и pg_repack --only-indexes лечат без блокировки

Вопросы для размышления

  • Если установить autovacuum_vacuum_scale_factor = 0 и autovacuum_vacuum_threshold = 100, что произойдёт с autovacuum поведением?
  • Почему таблица после VACUUM FULL может быть быстрее чем после pg_repack? Подсказка: порядок строк на диске.
  • txid wraparound - pg_database.datfrozenxid достигает 2^31. Что сделает PG если не запустить VACUUM FREEZE вовремя?

Связанные уроки

  • db-09-indexes-btree
VACUUM: сборка мусора и autovacuum

0

1

Войти