awesome-everything EN
↑ Обратно к восхождению

Базы данных

VACUUM, bloat и autovacuum

Суть Каждый UPDATE пишет новый tuple; мёртвые версии накапливаются пока VACUUM их не уберёт. VACUUM никогда не сокращает файл — только помечает слоты. VACUUM FULL и pg_repack сокращают. Autovacuum запускается по формуле threshold + scale_factor × n_live_tup.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 16 min

Таблица orders занимает 180 ГБ вместо ожидаемых 80 ГБ. Autovacuum запускался 14 раз за ночь. Диск полон к 7 утра. Почему VACUUM не помог — и как остановить это в следующий раз?

Откуда берётся bloat

Каждый UPDATE создаёт новую версию tuple плюс помечает старую мёртвой — оба на диске. До коммита оба существуют. После коммита мёртвая версия остаётся на диске, пока VACUUM не решит, что она больше никому не нужна.

Модель затрат:

ОперацияHeapИндексы
INSERT+1 tuple+1 запись на индекс
UPDATE (не HOT)+1 живой + 1 мёртвый tuple+1 запись на индекс
UPDATE (HOT)+1 живой + 1 мёртвый tuple0 новых записей
DELETE+1 мёртвый tuple(запись остаётся до VACUUM)

Пока VACUUM не уберёт мёртвые tuple, каждый sequential scan пробегает мимо них, каждый индексный lookup может на них наткнуться, и каждая страница больше необходимого минимума.

Правило большого пальца: целевая доля мёртвых tuple менее 20% для таблиц больше 50 ГБ.

Три варианта уборки

ОперацияЧто делаетБлокировкаДиск
VACUUMПомечает мёртвые tuple переиспользуемымиSHARE UPDATE EXCLUSIVE (чтения/записи продолжаются)Файл не сокращается
VACUUM FULLПереписывает всю таблицуACCESS EXCLUSIVE (блокирует всё)Возвращает диск ОС
pg_repackПереписывает онлайнТолько короткий ACCESS EXCLUSIVE на финальный swapВозвращает диск ОС

Как autovacuum решает запускаться

Autovacuum — фоновый пул процессов (3 воркера по умолчанию), просыпающийся каждую минуту и проверяющий каждую таблицу на пороговое условие:

autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor × n_live_tup

Дефолт: 50 + 20% живых tuple. Когда таблица переходит порог, autovacuum посылает воркера.

Шаги одного прогона autovacuum:

Расставь шаги по порядку

Поставь шаги одного прогона autovacuum в правильном порядке:

  1. 1 Планировщик просыпается; проверяет n_dead_tup против threshold + scale_factor × n_live_tup
  2. 2 Воркер подключается; берёт SHARE UPDATE EXCLUSIVE (параллельные чтения и записи продолжаются)
  3. 3 Вычисляет глобально старейший xmin по всем сессиям — только tuple старше можно убирать
  4. 4 Скан heap: обходит каждую страницу, выявляет мёртвые tuple с t_xmax < oldest xmin
  5. 5 Помечает слоты мёртвых tuple переиспользуемыми; перестраивает free-space map
  6. 6 Чистка индексов: удаляет записи, ссылающиеся на освобождённые heap-слоты
  7. 7 Обновляет pg_class.relfrozenxid + счётчики pg_stat_all_tables; отпускает блокировку

Oldest xmin: почему долгие транзакции блокируют уборку

Autovacuum может убирать только tuple, чей t_xmax старше глобально старейшего xmin по всем сессиям. Если одна транзакция держится открытой, её backend_xmin пинит oldest-xmin.

Диагностика:

SELECT pid, backend_xmin,
       now() - xact_start AS duration,
       state, query
FROM pg_stat_activity
WHERE backend_xmin IS NOT NULL
ORDER BY backend_xmin
LIMIT 5;

Самый старый backend_xmin — это то, что блокирует уборку.

Нюанс hot_standby_feedback

Стриминговые реплики можно настроить с hot_standby_feedback = on, которая отсылает primary старейший активный xmin реплики. Без неё долгий аналитический запрос на реплике может упасть с canceling statement due to conflict with recovery.

С ней bloat primary — заложник самого долгого запроса реплики. Большинство production-сетапов выбирают off на аналитической реплике и принимают периодические отмены запросов — закрепление bloat primary навсегда хуже.

Числа

VACUUM и bloat: ключевые числа
Bloat-цель (>50 ГБ)
<20% мёртвых tuple
Дефолты autovacuum
3 воркера, 60с naptime, 20% scale
Блокировка VACUUM FULL
ACCESS EXCLUSIVE
Размер заголовка tuple
23 Б (+ выравнивание)
XID ширина
32 бита; заморозка на 2^31
pg_repack онлайн-стоимость
временно 2x disk

Проверь себя

Викторина

Таблица orders занимает 180 ГБ. Ты запустил VACUUM и он завершился без ошибок. Теперь df показывает... что?

Викторина

Autovacuum запускался 10 раз за ночь, но n_dead_tup не падает. Какая самая вероятная причина?

Викторина

Когда hot_standby_feedback = on на реплике, что происходит с primary?

Вспомните перед уходом
  1. 01
    Почему обычный VACUUM не возвращает место ОС, даже если убрал миллионы мёртвых tuple?
  2. 02
    Долгая батч-задача накопила 10 ГБ мёртвых tuple на таблице orders. Autovacuum запускался, но n_dead_tup не падает. Что вероятная причина и как её подтвердить?
  3. 03
    Когда стоит выбрать VACUUM FULL вместо pg_repack?
Recap
  • Каждый UPDATE пишет новый tuple + помечает старый мёртвым; mёртвые версии накапливаются до прохода VACUUM
  • VACUUM помечает слоты переиспользуемыми; не сокращает файл и не возвращает диск ОС
  • VACUUM FULL возвращает диск под ACCESS EXCLUSIVE (блокирует всё); pg_repack — онлайн аналог
  • Autovacuum запускается по threshold + scale_factor × n_live_tup; cost-based rate-limit контролирует IO
  • Oldest-xmin — потолок уборки; долгая транзакция или orphan slot пинит его для всей базы
  • hot_standby_feedback = on на реплике → primary bloat заложник самого долгого запроса реплики
Связанные уроки
встречается в140
Продолжить восхождение ↑CLOG, XID wraparound и MultiXact
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.