Миллион удалённых строк в одной таблице может потребовать совсем разного времени очистки. В эксперименте Percona одинаковый объём удалений давал заметно разные результаты в зависимости от того, как строки лежали на страницах. Для дежурного инженера это полезная поправка: график dead tuples показывает накопившуюся работу лишь приблизительно. По нему трудно понять, успеет ли autovacuum закончить до следующего пика нагрузки.

Эксперимент, который стоит повторить на стенде

Pep Pla сравнил два способа удаления данных. В первом удалялся непрерывный диапазон идентификаторов из последовательно заполненной таблицы. Во втором удаляемые строки были распределены по всей таблице. Количество строк совпадало, но во втором случае очистка затрагивала гораздо больше страниц.

На стенде использовался PostgreSQL 18.4, Ubuntu 24.04, четыре выделенных vCPU и 15 GiB RAM. Таблица содержала десять миллионов строк. Весь рабочий набор помещался в shared buffers; перед измерениями данные замораживались, конкурентных запросов не было. Автор менял долю удалений и число одинаковых B-tree индексов, повторяя каждую комбинацию десять раз.

При удалении 10% строк и отсутствии индексов медиана составила 5 секунд для компактного удаления и 43,6 секунды для распределённого. Это результат конкретного стенда, а не коэффициент для расчёта production. Полезен сам контроль переменных: одинаковый счётчик удалённых строк ещё не означает одинаковую работу по очистке.

Добавление индексов увеличивало её стоимость. Здесь легко сделать лишний шаг и решить, что любой индекс добавляет фиксированное время. Эксперимент этого не доказывает: индексы были одинаковыми, по одному целочисленному столбцу. Широкий составной индекс, другой тип ключа или иной размер рабочего набора изменят картину.

Почему счётчик строк не объясняет длительность

PostgreSQL сохраняет версии строк для MVCC. После UPDATE или DELETE прежнюю версию нельзя убрать, пока она может понадобиться транзакции. VACUUM освобождает место для повторного использования, когда это уже допустимо. Поэтому сначала стоит отделить две ситуации: очистка медленно выполняет доступную работу или часть версий пока нельзя удалить.

Во втором случае увеличение ресурсов может почти ничего не дать. Долгая транзакция удерживает старый снимок, а воркеры продолжают встречать версии, которые ещё нужны читателю. Прежде чем менять параметры, проверьте длительные транзакции и причины удержания горизонта очистки. Не завершайте их автоматически: сначала установите владельца и последствия отмены.

Есть и другой частый тупик: ожидать, что после обычного VACUUM файл таблицы обязательно уменьшится. Освобождённое внутри файла пространство обычно остаётся для будущих записей. Уменьшение файла возможно при освобождении его хвоста; это не равно полной перепаковке таблицы. Рост свободного места внутри отношения и снижение размера на файловой системе отвечают на разные вопросы.

Официальная документация PostgreSQL 18 о регулярной очистке описывает также visibility map: она помогает пропускать страницы, которые не требуют обработки. Поэтому физическое распределение изменений влияет на объём работы даже при совпадающем количестве удалений.

Сначала разберите один медленный запуск

Я бы начал с одной таблицы, которая регулярно мешает сервису. Глобальное увеличение числа воркеров плохо подходит для первого эксперимента: оно меняет конкуренцию за ресурсы сразу для нескольких таблиц и затрудняет объяснение результата.

Сопоставьте время запуска и завершения очистки с нагрузкой приложения. Посмотрите, росла ли очередь диска, изменялась ли задержка запросов, были ли долгие транзакции. Отдельно оцените размер индексов. Таблица с умеренным heap и крупными индексами может требовать значительной работы вне основного файла данных.

Статистика должна охватывать несколько циклов. Один короткий запуск после спокойной ночи мало говорит о поведении во время массовых обновлений. Оценки количества строк тоже не превращайте в точный счётчик: выводы надёжнее, когда статистика таблицы согласуется с журналом autovacuum и наблюдаемой нагрузкой.

Для следующего изменения запишите исходные условия: версию PostgreSQL, настройки таблицы, размеры heap и индексов, профиль записей и параллельную нагрузку. Этого достаточно, чтобы через неделю отличить эффект настройки от изменения самого трафика.

Что менять и как проверять эффект

Порог запуска определяет, когда таблица становится кандидатом на очистку. Он не гарантирует немедленного начала или определённой длительности. Если таблица систематически накапливает слишком много работы между проходами, рассмотрите её индивидуальные настройки autovacuum. Применяйте их по одному и проверяйте несколько последующих циклов.

Ресурсные параметры отвечают за другую часть задачи. В справочнике настроек PostgreSQL 18 описаны число воркеров, ограничения стоимости и задержки. Бюджет стоимости может распределяться между работающими воркерами; добавление процессов само по себе не обещает пропорционального ускорения.

Память тоже требует расчёта. При её нехватке очистке индексов могут понадобиться дополнительные проходы. Но увеличение лимита для каждого воркера увеличивает потенциальное общее потребление. Сравнивайте выигрыш по времени с запасом памяти сервера, учитывая запросы приложения и другие задачи обслуживания.

Не переносите настройки лабораторного стенда в рабочий кластер целиком. В исходном эксперименте пороги специально заставляли очистку запускаться почти сразу, чтобы измерить отдельный проход. Такие значения служат методике измерения, а не универсальному эксплуатационному профилю.

Как построить полезный повторный тест

Возьмите обезличенный набор, близкий к рабочей таблице по ширине строк, индексам и объёму. На изолированном стенде сравните компактные и распределённые изменения при одинаковом количестве затронутых строк. Затем добавьте обычную для сервиса конкурентную нагрузку.

Сохраняйте одинаковое начальное состояние между вариантами. Иначе один запуск получит прогретый кеш и уже очищенные страницы, а другой будет оплачивать подготовительную работу. Отмечайте отдельно время ожидания запуска и время самой очистки. Повторяйте измерения и смотрите разброс, а не только лучший результат.

Критерий успеха задайте со стороны сервиса: очистка успевает за появлением ненужных версий, а задержки запросов остаются допустимыми. Более быстрый VACUUM, который заметно ухудшил обслуживание клиентов, не решает исходную задачу.

Ограничения и вывод

Бенчмарк Percona исследует удобный для анализа случай: одна таблица в памяти, без конкуренции и без накопленной работы по freezing. Он не проверяет очередь таблиц, перегруженное хранилище или агрессивную очистку перед исчерпанием пространства идентификаторов транзакций. Эти сценарии требуют отдельных проверок.

Практический вывод для runbook: рядом с dead tuples держите длительность очистки, размеры индексов, признаки долгих транзакций и нагрузку на хранилище. Настраивайте наиболее проблемную таблицу и сохраняйте результаты нескольких циклов. Так очередное увеличение лимита станет проверяемым изменением, а не попыткой угадать причину по одному графику.

Источник и лицензия

Pep Pla, Percona Community Blog: PostgreSQL Autovacuum Internals and Benchmark, 1 июля 2026 года. Корпоративная площадка Percona; автор указан в каталоге как Senior Consultant Percona PS.

Исходный материал распространяется по CC BY 4.0 согласно правилам лицензирования Percona Community Blog. Выполнена самостоятельная русская адаптация: структура и объём изменены, добавлены редакционные рекомендации и ссылки на документацию. Бенчмарк редакцией не повторялся; его результаты приведены по публикации автора. Автор и Percona не заявляли об одобрении этой адаптации.