Опирается на правила:
PG-V-001…PG-V-080из PostgreSQL Style Guide → раздел VACUUM, autovacuum и bloat.
Важно знать
- MVCC:
UPDATE/DELETEпомечают старую версию мёртвой, пишут новую. Dead tuples копятся.- VACUUM освобождает место в FSM, обновляет visibility map, предотвращает XID wraparound.
- VACUUM не возвращает место ОС — таблица не уменьшается на диске.
- VACUUM не блокирует чтение/запись (SHARE UPDATE EXCLUSIVE).
- VACUUM FULL почти никогда в проде (ACCESS EXCLUSIVE). Альтернатива —
pg_repack.- autovacuum работает автоматически, default
scale_factor 0.2.- На больших горячих таблицах — снизь
scale_factorдо 0.05.- Bloat:
dead_pct > 20%вpg_stat_user_tables;pgstattupleдля точной картины.fillfactor 80-90на write-heavy для HOT updates.- autovacuum не справляется: долгие транзакции, replication slots, prepared TX.
PostgreSQL использует MVCC — dead tuples копятся, VACUUM их убирает. UCP формулирует — когда autovacuum хватает, когда нет.
Зачем VACUUM
PG-V-001..002: три задачи.
- Освобождает место от dead tuples — заносит в FSM (Free Space Map), новые INSERT занимают. Не возвращает место ОС.
- Обновляет visibility map — карта страниц, где все tuples видны всем транзакциям. Позволяет Index Only Scan работать без Heap Fetches.
- Предотвращает XID wraparound — 32-битный счётчик, через 2 млрд транзакций «проходит круг»; VACUUM «замораживает» старые tuples.
Не блокирует чтение/запись (SHARE UPDATE EXCLUSIVE). Только DDL (ALTER TABLE) ждёт.
Три варианта
| Команда | Что | Lock | Возвращает место ОС |
|---|---|---|---|
VACUUM | Помечает dead tuples в FSM | SHARE UPDATE EXCLUSIVE | Нет |
VACUUM FULL | Перезаписывает таблицу с нуля | ACCESS EXCLUSIVE (блокирует ВСЁ) | Да |
VACUUM ANALYZE | VACUUM + статистика | SHARE UPDATE EXCLUSIVE | Нет |
VACUUM FULL — почти никогда
PG-V-010..011: ACCESS EXCLUSIVE блокирует всех на минуты-часы. Только в окне обслуживания.
Альтернатива — pg_repack / pg_squeeze (re-write без блокировки).
VACUUM ANALYZE — после массовой загрузки/обновления, не жди autovacuum.
autovacuum
PG-V-020..022: работает автоматически.
Дефолтные пороги (PG14+):
autovacuum_vacuum_threshold = 50— минимум dead tuples.autovacuum_vacuum_scale_factor = 0.2— плюс 20% от размера таблицы.- Триггер:
dead_tuples > threshold + scale_factor × n_live_tup.
На 1M строк autovacuum запустится при ~200K dead. Для большинства — норм.
На больших горячих таблицах дефолт мал:
ALTER TABLE order_doc SET (
autovacuum_vacuum_scale_factor = 0.05,
autovacuum_analyze_scale_factor = 0.05
);
Теперь VACUUM запустится при ~50K dead — реже копится bloat.
Bloat — как обнаружить
PG-V-030..032:
SELECT relname,
n_live_tup,
n_dead_tup,
round(100.0 * n_dead_tup / NULLIF(n_live_tup, 0), 1) AS dead_pct,
last_autovacuum,
last_autoanalyze
FROM pg_stat_user_tables
WHERE n_live_tup > 1000
ORDER BY n_dead_tup DESC LIMIT 10;
dead_pct > 20%— кандидат на VACUUM.last_autovacuumстарше суток на горячей — autovacuum не успевает.
Точная картина — pgstattuple:
CREATE EXTENSION pgstattuple;
SELECT * FROM pgstattuple('order_doc');
-- dead_tuple_percent > 30% — распухла
Bloat индексов — отдельная история. Лечится REINDEX CONCURRENTLY (PG12+) — не блокирует таблицу.
fillfactor
PG-V-040: на write-heavy.
ALTER TABLE order_doc SET (fillfactor = 85);
VACUUM FULL order_doc; -- переразложит (один раз, в окно)
С fillfactor < 100 страницы заполняются не до конца, остаётся место под HOT (Heap-Only Tuple) — UPDATE без перемещения строки. Подробно — WAL.
Когда autovacuum не справляется
PG-V-050..053:
Долгая транзакция
PG не может пометить tuple свободным, пока его видит хоть одна открытая TX (MVCC).
SELECT pid, age(now(), xact_start) AS xact_age, state, query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY xact_age DESC LIMIT 10;
xact_age > 5 минут — алёрт. Обычно сломанная сессия IDE, висящий тест.
Висящий replication slot
Если читатель WAL отстал, PG не освобождает WAL → autovacuum не освобождает dead tuples.
Prepared transaction
SELECT * FROM pg_prepared_xacts;
Живут до явного COMMIT PREPARED / ROLLBACK PREPARED.
autovacuum выключен
SELECT name, setting FROM pg_settings WHERE name = 'autovacuum';
-- per-table
SELECT relname, reloptions FROM pg_class
WHERE relkind = 'r' AND reloptions::text LIKE '%autovacuum%';
Иногда выключают для bulk-load и забывают вернуть.
VACUUM в миграциях
PG-V-060..061:
После big UPDATE/DELETE:
UPDATE order_doc SET status = 'ARCHIVED' WHERE created_at < now() - interval '1 year';
VACUUM ANALYZE order_doc;
Иначе планировщик долго будет считать most_common_vals неверными.
CREATE INDEX CONCURRENTLY обновляет visibility map — после построения для Index Only Scan нужен VACUUM, чтобы Heap Fetches упали до 0.
VACUUM FULL — когда оправдан
PG-V-070: только в окно обслуживания при сильном bloat (>50%).
Сценарии:
- После массового
DELETE>30% таблицы. - После изменения
fillfactor. - Bloat через
pgstattuple, обычный VACUUM не помогает (постоянная нагрузка UPDATE).
Альтернатива без блокировки:
pg_repack -d mydb -t order_doc
XID wraparound
PG-V-080: 32-битный счётчик, 2.1 млрд значений.
SELECT datname,
age(datfrozenxid) AS xid_age,
2147483647 - age(datfrozenxid) AS xids_left
FROM pg_database;
Алёрт: xid_age > 1.5 млрд — autovacuum не справляется.
Чек-лист «таблица распухла»
n_dead_tup> 20% отn_live_tup→ autovacuum не отрабатывает.last_autovacuumдавно → долгие TX, replication slots,autovacuum_enabled.pgstattuple dead_tuple_percent > 30%→VACUUM <table>руками. Если не помогает —autovacuum_vacuum_scale_factor.fillfactorдефолтный на write-heavy → 80-90.- Окно обслуживания +
pg_repack. - Bloat индексов →
REINDEX CONCURRENTLY.
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
VACUUM FULL в проде онлайн | PG-V-010 | pg_repack |
autovacuum = off глобально | PG-V-053 | оставить включённым |
Дефолт scale_factor 0.2 на больших горячих | PG-V-021 | 0.05 |
Игнорировать n_dead_tup | PG-V-030 | мониторинг |
fillfactor 100 на write-heavy | PG-V-040 | 80-90 + VACUUM FULL один раз |
Долгая TX без idle_in_transaction_session_timeout | PG-V-050 | 30s timeout |
Bloat индексов без REINDEX CONCURRENTLY | PG-V-032 | PG12+ rebuild |
Не делать VACUUM ANALYZE после big UPDATE | PG-V-060 | руками |
Куда дальше
- PG → VACUUM — нормативные формулировки.
- WAL — fillfactor и HOT updates.
- Composite-индексы — Heap Fetches и visibility map.
- Расширения —
pgstattuple,pg_repack. - Уровни изоляции —
idle_in_transaction_session_timeout. - Мониторинг —
pg_stat_user_tables.