PostgreSQL 14 в production: два месяца с новым vacuum и наша конфигурация
Перевели первого клиента на PostgreSQL 14 - делимся конфигурацией autovacuum и наблюдениями за bloat за первые два месяца эксплуатации в продакшне.
PostgreSQL 14 GA (октябрь 2021): улучшенный vacuum с aggressive mode, pipeline-режим клиентов, улучшения партиционирования
В октябре мы перевели первый DWH-кластер на PostgreSQL 14 и в основном писали про LZ4-сжатие на TOAST. Vacuum тогда упомянули вскользь - «работает лучше, смотрим дальше». Прошло два месяца. Вот что мы увидели и как в итоге выглядит наша конфигурация.
Что изменилось в vacuum в PG14
Главное для нас - vacuum_failsafe_age и изменённое поведение autovacuum в aggressive mode. В PostgreSQL 14 autovacuum стал агрессивнее реагировать на таблицы с накопившимся bloat: при достижении порогов autovacuum_vacuum_scale_factor и autovacuum_vacuum_insert_scale_factor он не ждёт следующего цикла, а приоритизирует такие таблицы внутри своей очереди. На практике это значит, что bloat стал сходить быстрее - без ручного вмешательства.
vacuum_failsafe_age - новый параметр, который форсирует агрессивный VACUUM при приближении к wraparound-границе, игнорируя любые cost_delay-ограничения. Звучит как крайняя мера - и это именно она. Но само её наличие меняет подход к настройке: раньше мы держали autovacuum_vacuum_cost_delay низким из паранойи по поводу wraparound. Теперь можно чуть ослабить дросселирование в штатном режиме, зная что failsafe подхватит в критической ситуации.
Кластер и нагрузка
Кластер - DWH одного из клиентов, аналитическая инфраструктура. Схема: несколько крупных таблиц фактов с партиционированием по месяцу, staging-область куда батчами прилетают исходные данные, несколько промежуточных таблиц для трансформаций. Запись идёт батчами каждые несколько минут через ETL-процессы; аналитические запросы - в течение рабочего дня. Объём базы порядка 450-500 ГБ.
На PostgreSQL 13 у нас регулярно возникал bloat на staging-таблицах: туда заливаются сырые данные, из них же читают и частично перезаписывают трансформации, и autovacuum не всегда успевал за этим темпом в рабочие часы. Приходилось держать ночное окно с ручным VACUUM на самых горячих таблицах.
Что увидели за два месяца
Bloat на staging-таблицах снизился заметно. Следим через pgstattuple на ключевых таблицах и смотрим на соотношение dead_tup_frac в pg_stat_user_tables. На PG13 staging-таблицы к утру показывали 15-20% мёртвых tuple; на PG14 это стабильно ниже 7-8% на том же паттерне нагрузки. Ночное ручное VACUUM убрали - autovacuum справляется сам.
Прогоны autovacuum стали короче, но чаще. pg_stat_user_tables.autovacuum_count растёт быстрее чем раньше - autovacuum запускается на таблицы раньше, не ждёт пока накопится много. Зато каждый прогон делает меньше работы. I/O-профиль изменился: вместо редких длинных burst'ов - более равномерный фон. Для аналитических запросов это лучше: они не попадают в момент, когда autovacuum гоняет 30 минут по горячей таблице.
vacuum_failsafe_age за два месяца не сработал ни разу. Хорошо - значит не дотягиваем до критической отметки. Параметр выставлен и мониторится: у нас есть алерт на age(relfrozenxid) по всем таблицам, failsafe там как дополнительный рубеж, не как основная защита.
Pipeline-режим libpq. Это не про vacuum, но раз уж про PG14 в целом: ETL-процессы, которые выполняют много мелких INSERT через Python-библиотеку psycopg3, получили ощутимое снижение latency батча за счёт pipelining. Не на всех клиентах ещё обновили psycopg3, но там где обновили - разница есть.
Наша конфигурация autovacuum
Параметры, с которыми работаем на этом кластере. Не универсальный рецепт - у нас DWH с батчевой записью, для OLTP с постоянными UPDATE цифры будут другими.
Глобальные параметры (postgresql.conf):
autovacuum_vacuum_scale_factor = 0.02- запускаем раньше, не ждём 20% мёртвых tuple (default 0.2)autovacuum_vacuum_insert_scale_factor = 0.05- для INSERT-heavy таблиц тоже снизили порогautovacuum_vacuum_cost_delay = 5ms- был 2ms, чуть ослабили дросселирование в расчёте на failsafeautovacuum_max_workers = 5- подняли с 3, кластер большойvacuum_failsafe_age = 1600000000- failsafe примерно за 550M транзакций до wraparoundautovacuum_freeze_max_age = 200000000- агрессивная заморозка начинается раньше
На уровне отдельных таблиц - staging-таблицы переопределяем через ALTER TABLE ... SET:
ALTER TABLE stg_events SET (
autovacuum_vacuum_scale_factor = 0.01,
autovacuum_vacuum_cost_delay = 2
);
Для таблиц фактов, куда после первоначальной загрузки практически нет UPDATE, scale_factor наоборот выше - 0.05 - чтобы autovacuum не тратил ресурсы на таблицы где bloat не накапливается.
Что не так радужно
Один неприятный момент: на паре таблиц с активным партиционированием autovacuum иногда пропускает партиции при быстрой ротации - успевает начать прогон, но к моменту завершения данные уже перенесены в следующую партицию. Не критично, но pg_stat_user_tables на таких таблицах показывает периодические выбросы n_dead_tup. Решили добавить явный VACUUM на последнюю партицию в конце каждого ETL-батча - не идеально, зато предсказуемо.
Где сейчас
Два месяца - не срок для окончательных выводов, но тенденция понятна: PG14 autovacuum действительно работает лучше на нашей нагрузке, и не нужно его сильно уговаривать настройками. Конфигурация устаканилась, ручное обслуживание сведено к минимуму.
Второй кластер на PG14 планируем перевести в феврале - там схема сложнее, партиций больше, посмотрим как поведёт себя там.