Почему VACUUM зависает на фазе индексов в PostgreSQL 15 и как это исправить
Ситуация, когда VACUUM в PostgreSQL 15 на большой таблице (2.4 ТБ) зависает в фазе vacuuming indexes на несколько суток, знакома многим администраторам баз данных. Это типичная проблема для сильно фрагментированных таблиц с крупными индексами. В статье разберём причины и дадим конкретные шаги для решения.
Почему VACUUM застревает на индексах
Фаза vacuuming indexes - самая ресурсоёмкая часть процесса вакуума. Она требует последовательного обхода каждого индекса и удаления ссылок на мёртвые строки. Если индекс большой (например, первичный ключ 690 ГБ или составной индекс 800 ГБ), а количество мёртвых кортежей достигает лимита max_dead_tuples (в вашем случае 178 956 969), вакуум может зависнуть, так как он вынужден многократно перечитывать одни и те же страницы.
Почему стандартные настройки не помогают
Увеличение maintenance_work_mem до 50 ГБ не дало эффекта, потому что память используется в основном для хранения битовой карты мёртвых кортежей, а не для ускорения обхода индекса. vacuum_cost_delay = 0 убирает задержки, но не решает проблему фрагментации. Даже перестроение индексов не помогло - это указывает на то, что проблема в механизме очистки, а не в самих индексах.
Как корректно очистить таблицу вместе с индексами
Шаг 1. Принудительная очистка без индексов
Запустите VACUUM (INDEX_CLEANUP OFF, TRUNCATE OFF) - это позволит быстро обработать основную таблицу, не тратя время на индексы. После этого все мёртвые строки будут помечены как свободные, но индексы останутся с «мусором».
Шаг 2. Параллельная очистка индексов
Используйте REINDEX INDEX CONCURRENTLY для каждого индекса по очереди. Это создаст новую версию индекса без блокировки записи в таблицу. После завершения старый индекс удалится автоматически.
REINDEX INDEX CONCURRENTLY table_pkey;
REINDEX INDEX CONCURRENTLY idx_composite;Шаг 3. Финальный VACUUM FULL
После перестроения индексов выполните VACUUM FULL - он компактизирует таблицу и вернёт место на диске. Однако учтите, что это заблокирует таблицу на время выполнения.
Какие ещё настройки стоит проверить
- work_mem - увеличьте до 256-512 МБ для ускорения сортировок при REINDEX.
- max_parallel_workers_maintenance - установите 2-4 для параллельного построения индексов.
- autovacuum_vacuum_scale_factor - уменьшите до 0.01, чтобы вакуум запускался чаще, но порциями.
- vacuum_freeze_min_age - уменьшите до 10000, чтобы избежать накопления мёртвых строк.
Почему затык именно на этой таблице
Скорее всего, таблица содержит большое количество обновлений (UPDATE) или удалений (DELETE), что приводит к накоплению мёртвых кортежей. Также возможно, что индексы имеют низкую селективность (например, составной индекс на int, timestamp), из-за чего вакуум вынужден обрабатывать много страниц. Рекомендуется настроить autovacuum на эту таблицу отдельно, задав более агрессивные параметры.
Профилактика для будущего
Чтобы VACUMM больше не зависал, внедрите следующие практики: регулярно запускайте REINDEX по расписанию (например, раз в неделю), настройте autovacuum с учётом размера таблицы, и рассмотрите секционирование - разбейте таблицу по дате или другому ключу, чтобы каждый сегмент был меньше 500 ГБ.