После массовой загрузки данных несколько крупных таблиц PostgreSQL одновременно стали кандидатами на autovacuum. В то же утро команда начала создавать индекс для нового отчёта и запустила ручное обслуживание другой таблицы. Каждая задача раньше проходила отдельно и считалась безопасной. Вместе они подняли расход памяти и создали очередь на накопителе. Обычные транзакции не остановились, но их p99 — граница, в которую укладывается подавляющее большинство ответов, — заметно ухудшился. Руководитель увидел привычную ловушку: штатные фоновые работы превратились в инцидент не из-за одной команды, а из-за календарного совпадения. maintenance_work_mem часто принимают за общий бюджет обслуживания сервера. На деле это предел отдельной операции, а число parallel workers внутри команды влияет на него не так, как в обычном запросе.
Предел относится к операции обслуживания
PostgreSQL использует maintenance_work_mem для таких работ, как обычный VACUUM, создание индекса и проверка внешнего ключа при его добавлении. Это другой класс памяти, чем work_mem из запросов: там речь шла о сортировках и хэш-операциях плана, здесь — о поддержании структуры и состояния базы. Параметр задаёт максимум, а не обязательную сумму, которая целиком резервируется при старте каждой команды. Фактическая потребность зависит от операции, таблицы, индексов и объёма работы. Маленькая таблица не обязана использовать весь разрешённый бюджет только потому, что предел высок.
В одной сессии одновременно выполняется одна такая команда, поэтому её лимит может быть выше обычного work_mem. Но на сервере бывают несколько администраторских сессий, autovacuum и восстановление логической выгрузки с построением индексов. У каждой независимой работы свой контур потребления. Именно независимость важна для бюджета. Два создания индекса — это две команды, ручной VACUUM и autovacuum worker — две работы, а обслуживание в разных базах одного кластера всё равно делит физическую RAM и накопитель хоста.
Пик создаёт совпадение самостоятельных работ
Автоматическое обслуживание реагирует на изменения данных, а не на календарь команды эксплуатации. После массовой загрузки или обновления несколько таблиц могут почти одновременно перейти порог, после которого им нужен VACUUM или ANALYZE. Плановая ночь не гарантирует, что к началу ручного создания индекса фоновые workers уже закончили. Ручное расписание тоже неполно: администратор восстанавливает выгрузку, приложение меняет данные, а autovacuum launcher запускает работников для созревших таблиц. Память — лишь половина конфликта. VACUUM создаёт I/O, построение индекса читает таблицу и пишет новую структуру, а транзакциям всё ещё нужны журнал и страницы данных.
Поэтому средний расход за день мало говорит о риске. Короткое совпадение нескольких работ способно съесть запас и насытить I/O, хотя каждая задача большую часть месяца запускается одна. Экономическая цена — не только возможный OOM, но и потерянная пропускная способность приложения во время обслуживания.
Autovacuum наследует бюджет не всегда очевидно
Для autovacuum существует отдельный предел autovacuum_work_mem, применяемый к каждому worker-процессу. Если его значение равно минус единице, worker наследует maintenance_work_mem. Большой общий предел тогда незаметно становится разрешением и для каждого одновременно работающего автоматического процесса. Документация PostgreSQL предупреждает, что при autovacuum потенциально может быть выделено до числа разрешённых autovacuum workers таких бюджетов. Это верхняя возможность, а не прогноз фактического расхода и не формула закупки RAM. Worker может использовать меньше, workers могут не совпасть во времени, а характер таблиц различается.
Когда несколько крупных таблиц требуют обработки, доступные workers могут надолго заняться ими. Они не входят в обычный предел клиентских подключений, но используют те же ресурсы хоста. Отдельный autovacuum_work_mem позволяет развести автоматический и ручной классы обслуживания. Смысл не в одном универсальном значении, а в том, чтобы высокий бюджет редкой контролируемой команды не стал без обсуждения общим бюджетом каждого фонового worker.
Parallel maintenance не умножает лимит команды
Самая опасная ложная аналогия приходит из parallel query. Для параллельных запросов work_mem применяется отдельно к worker-процессам, поэтому общий расход может существенно вырасти. У поддерживаемых параллельных utility-команд PostgreSQL действует иное правило. При параллельном создании индекса и других поддерживаемых utility-операциях maintenance_work_mem ограничивает команду целиком независимо от числа parallel maintenance workers. Нельзя считать, что каждый worker получает ещё один полный maintenance_work_mem. Документация прямо отделяет эту модель от параллельных запросов.
Параллельность всё равно использует процессор и способна увеличить интенсивность I/O. Индекс может строиться быстрее, но сильнее конкурировать с транзакциями за чтение и запись. Следовательно, источник пика нужно называть точно. Несколько workers внутри одного CREATE INDEX — не то же самое, что несколько независимых CREATE INDEX в разных сессиях. В первом случае память ограничена бюджетом команды; во втором каждая команда имеет собственную операцию и может совпасть с autovacuum.
Большой и маленький бюджет имеют разную цену
Более высокий maintenance_work_mem может ускорить поддерживаемую операцию. Для VACUUM доступный объём влияет на то, сколько данных о мёртвых строках можно собрать до очистки индексов; тесный бюджет способен привести к дополнительным проходам и более долгой нагрузке на I/O. Но выгода не растёт автоматически вместе с пределом. Операция может упираться в чтение таблицы, CPU или накопитель и не использовать весь разрешённый объём. Если общий default велик, несколько независимых работ получают право на больший расход именно в момент совпадения.
Слишком маленькое значение тоже имеет бизнес-цену: обслуживание длится дольше, дольше конкурирует с приложением и может не укладываться в доступное окно. Слишком большое повышает риск короткого пика памяти и давления на соседние процессы. Выбор находится между продолжительностью работ и безопасностью их совместного исполнения. Отдельно важно не смешивать обычный VACUUM и VACUUM FULL. Стандартный VACUUM работает рядом с производственной нагрузкой, хотя создаёт I/O. VACUUM FULL переписывает таблицу и требует сильной блокировки; autovacuum такую команду вообще не запускает. Эта разница влияет на расписание, но не превращает статью о памяти в совет использовать FULL.
Расписание обслуживания — договор о ресурсах
Покупка дополнительной DRAM оправдана, если обязательные независимые работы должны выполняться одновременно и их нельзя развести без нарушения сроков обслуживания. Но часто дешевле изменить само совпадение: отделить бюджет autovacuum от ручных команд, сократить одновременность или перенести построение индекса из окна массовой загрузки. Это не означает откладывать VACUUM бесконечно. Автоматическое обслуживание защищает базу от накопления устаревших версий строк и других рисков, поэтому его нельзя оценивать как необязательный фон. Управленческая задача — дать ему завершаться, не позволяя нескольким тяжёлым работам без контроля делить один и тот же запас.
Представления прогресса VACUUM и CREATE INDEX показывают активную фазу, но не точную RAM команды. Решение связывает календарь с общей памятью процессов PostgreSQL, I/O и качеством приложения. Прозрачный страничный уровень класса gigaRAM не управляет расписанием обслуживания. Рабочие структуры CREATE INDEX и VACUUM во время операции активны и не являются очевидными кандидатами для переноса на NVMe. Страничный слой может обслуживать другие подходящие менее активные страницы, оставляя горячие в DRAM, но бюджет и одновременность VACUUM, CREATE INDEX и autovacuum по-прежнему контролирует PostgreSQL.
Главный вывод: maintenance_work_mem — бюджет операции, а не всего сервера и не каждого parallel worker одной команды. Риск формируют независимые команды, отдельные autovacuum workers и их совпадение с транзакциями. Поэтому хороший бюджет начинается с календаря обязательных работ и допустимой конкуренции, а не с механического умножения настройки.
Источники. PostgreSQL 18 — Resource Consumption определяет maintenance_work_mem, наследование autovacuum_work_mem и единый лимит parallel utility command.
Routine Vacuuming описывает workers, I/O обычного VACUUM и то, что autovacuum не запускает VACUUM FULL.
CREATE INDEX подтверждает общий бюджет параллельного построения индекса.
Progress Reporting используется только для смысла фаз VACUUM и CREATE INDEX.
Meta Engineering — TMO подтверждает общую механику страничного переноса, а продуктовая страница — только позиционирование класса решения.
