PostgreSQL 16 GA: планируем миграцию с PG 14 и тестируем parallel VACUUM на секционированных таблицах
PostgreSQL 16 вышел GA 14 сентября. Тестируем parallel VACUUM и INSERT throughput на секционированных таблицах перед миграцией production-кластера с PG 14.
PostgreSQL 16 GA выпущен - улучшенная логическая репликация, параллельный VACUUM, pg_stat_io, 14 сентября 2023
PostgreSQL 16 должен выйти GA 14 сентября - это уже не предположение, а официальный release candidate с зафиксированной датой. Мы к этому моменту готовимся заранее: у нас в managed-сопровождении есть несколько кластеров на PG 14, и один из них - хороший кандидат на миграцию уже в четвёртом квартале. Пока бета превращалась в RC, мы гоняли стендовые тесты. Вот что реально нашли.
Почему именно с 14, а не с 15
PG 14 у клиента стоит крепко: установлен полтора года назад, схема устоялась, настройки выверены. PG 15 вышел в прошлом году, но тогда у нас не было достаточного аргумента для планового апгрейда - улучшения в merge и row-filtering для логической репликации были интересны, но не критичны под конкретный профиль нагрузки.
PG 16 даёт два конкретных довода:
- Параллельный VACUUM для секционированных таблиц. У клиента основная таблица событий секционирована по месяцам. В PG 14 VACUUM идёт по секциям последовательно - при большом количестве активных секций это заметно. В PG 16 VACUUM умеет работать с несколькими секциями параллельно.
pg_stat_io. Мы уже видели этот инструмент в бете Postgres Pro 16 на другом стенде. Он меняет диагностику I/O принципиально - вместо косвенной корреляцииpg_stat_bgwriter+iostatполучаешь прямую разбивку по backend_type и context. На продакшне такого нам не хватало.
Логическая репликация тоже улучшилась, но клиент её не использует, так что это бонус, а не аргумент.
Стенд и что мы меряли
Стенд - реплика схемы клиентского кластера: PostgreSQL на выделенной машине, основная таблица events секционирована по 12 месяцам, в каждой секции от 20 до 80 млн строк, индексы на нескольких полях. Нагрузка - синтетика, но воспроизводящая реальный профиль: массовые пакетные INSERT в текущую секцию плюс периодические аналитические запросы по старым.
Сравниваем PG 14.9 и PG 16 RC1.
Parallel VACUUM
В PG 14 принудительный VACUUM на всей партиционированной таблице:
VACUUM (VERBOSE, ANALYZE) events;
Идёт последовательно секция за секцией - это видно в pg_stat_progress_vacuum, одна запись активна в каждый момент времени.
В PG 16 параллельный VACUUM распространился на секционированные таблицы: VACUUM теперь может запускать параллельных воркеров на разные секции одновременно. Управляется через max_parallel_maintenance_workers.
На нашем стенде при трёх параллельных воркерах общее время VACUUM по всей таблице сократилось примерно вдвое по сравнению с PG 14. На активных боевых секциях с интенсивной записью эффект ощутимее - там bloat накапливается быстрее и воркерам есть чем заняться параллельно.
Одна оговорка: параллелизм VACUUM создаёт дополнительную нагрузку на I/O. На нашем стенде пиковая нагрузка на диск во время VACUUM выросла пропорционально числу воркеров. Если кластер живёт на дисках без запаса по IOPS - нужно аккуратнее выбирать время и степень параллелизма, а не просто выкрутить воркеры на максимум.
INSERT throughput на секциях
Здесь ожидали меньшего, но проверили на всякий случай. PG 16 принёс ряд оптимизаций в планировщик для секционированных таблиц - уменьшение overhead на partition pruning при INSERT в большую схему.
На нашем стенде с 12 секциями разница при одиночных INSERT практически в пределах погрешности. При пакетных вставках через COPY или батчи по несколько тысяч строк - чуть заметнее, но не радикально. На схеме с сотнями секций эффект был бы, вероятно, более выраженным - у нас их 12, и PG 14 с этим справлялся без видимых проблем.
Вывод: для нашего конкретного кейса INSERT - не аргумент миграции. Parallel VACUUM - аргумент.
pg_stat_io в деле
После перехода стенда на PG 16 первым делом подключились к pg_stat_io. Уже в первые часы прогона под нагрузкой стало видно, что autovacuum генерирует непропорционально много read-операций на старых секциях - там индексы пухлые, таблица большая, и autovacuum пытается сделать с ними что-то разумное.
В PG 14 это пришлось бы вылавливать через pg_stat_bgwriter + iostat + корреляцию по времени. Здесь всё видно напрямую: кто, что, сколько, в каком контексте. Мы скорректировали autovacuum_vacuum_cost_delay на конкретных секциях - и нагрузка на диск в часы пиковых аналитических запросов стала ровнее.
Что дальше
GA 14 сентября - после этого даём релизу пару недель, смотрим на первые сообщения сообщества. Если ничего критичного не всплывает, начинаем готовить план миграции production-кластера: pg_upgrade или логическая репликация с минимальным downtime. У клиента есть требование по окну обслуживания, так что выбор метода ещё не финален.
Параметры max_parallel_maintenance_workers и профиль нагрузки на I/O во время VACUUM надо будет откалибровать уже на реальном железе - стенд есть стенд, там одна нода и нет боевого трафика. Но направление понятно: PG 16 для этого кластера имеет смысл, и тест это подтвердил.