ADG Оставить заявку
Блог Данные и аналитика 4 мин чтения

PostgreSQL 18 в продакшне: месяц спустя после GA

Инкрементальные чекпоинты убрали пики I/O, async I/O ускорил сканирование - разбираем реальные эффекты PG18 в продакшне и один неожиданный регресс в VACUUM.

Контекст момента

Команды завершают переход PostgreSQL 18 в продакшн, январь 2026 - месяц после GA

В декабре мы перевели первый клиентский контур на PostgreSQL 18 в настоящий продакшн - не стенд, не shadow-трафик, а живая нагрузка. Сейчас январь, прошло около месяца с GA, и накопилось достаточно наблюдений, чтобы написать что-то осмысленное. Не «18-я быстрее 17-й» - это мы знали ещё по RC. А что именно изменилось в повседневной операционной жизни базы.

Инкрементальные чекпоинты: это была боль, которую мы не замечали

Если честно, до перехода мы не ожидали от инкрементальных чекпоинтов особого эффекта - казалось, это «техническая деталь для очень нагруженных систем». Реальность оказалась другой.

На финтех-профиле с высокой частотой записи (короткие транзакции, постоянный поток UPDATE) у нас раньше был характерный паттерн: раз в checkpoint_completion_target * checkpoint_timeout секунд - небольшой, но заметный всплеск latency. На графиках это выглядело как регулярные «зубья». Мы к этому привыкли, считали нормой и компенсировали через checkpoint_completion_target = 0.9 и растянутые интервалы.

После перехода на PG18 зубья исчезли. Не уменьшились - именно исчезли. Инкрементальные чекпоинты размазывают запись dirty pages равномерно, и вместо периодических пиков I/O получается ровный фоновый поток. На мониторинге это хорошо видно по pg_stat_bgwriter - buffers_checkpoint теперь накапливается равномернее, без пульсаций.

Единственное, что нужно сделать вручную - убедиться, что wal_level не ниже replica. Из коробки инкрементальные чекпоинты не включены по умолчанию - надо явно выставить checkpoint_flush_after. Документация это объясняет, но в старом конфиге этих параметров нет, и их легко пропустить при апгрейде.

Async I/O в продакшне: ожидание vs реальность

В RC и на стендах мы видели +18-20% throughput при io_method=io_uring на нагрузках с реальным I/O-давлением. В продакшне цифра чуть скромнее - порядка 12-15% на том же профиле. Причина понятная: на стенде нагрузка синтетически ровная, в продакшне есть периоды низкой активности, праздники, ночные окна - они усредняют результат.

Но дело не только в средних значениях. Важнее другое: Sequential scan по большим таблицам ускорился ощутимо. Ночные ETL-джобы, которые проходят несколько таблиц целиком, стали заканчиваться раньше. Не в два раза - заметно, но без магии. Зато стабильно и воспроизводимо от запуска к запуску.

Параллельные seq scan с io_method=io_uring - это отдельная история. Когда несколько рабочих процессов параллельно читают разные диапазоны таблицы, async I/O позволяет им не ждать друг друга на I/O-барьерах. На таблицах от 50 GB разница становится заметной.

Конфигурация, которую мы зафиксировали для этого профиля:

  • io_method = io_uring - ядро 6.1+, io_uring не заблокирован seccomp. На старых ядрах или в контейнерах с ограниченным io_uring - worker.
  • max_io_concurrency = 32 - дефолт 16, на NVMe стоит поднять.
  • effective_io_concurrency = 200 - теперь это hint планировщику, а не ограничитель; для NVMe смысл выставить реалистичное значение.

Неожиданный регресс: VACUUM стал вести себя странно

Вот это мы не ожидали. На одном из контуров начали замечать, что autovacuum иногда затягивается на таблицах средних размеров - не гигантских, а именно тех, где раньше он проходил быстро и незаметно. Сначала решили, что это артефакт нагрузки. Покопались глубже.

Оказалось, что при io_method=io_uring autovacuum ведёт себя иначе с точки зрения I/O-приоритетов. Конкретно - при высокой конкурентной нагрузке vacuum-воркеры могут «вытесняться» на уровне io_uring-очереди более активными backend-процессами. В итоге vacuum не завершает проход за один цикл, откладывается, накапливает работу и потом приходит с более тяжёлым прогоном.

Это не баг в привычном смысле - это поведение, которое проявляется при определённом сочетании нагрузки и конфигурации. Лечится двумя вещами:

  • autovacuum_vacuum_cost_delay = 2ms вместо дефолтных 2ms... подождите, дефолт уже 2ms. На самом деле помогло снижение autovacuum_vacuum_cost_limit до 400 при одновременном увеличении autovacuum_max_workers до 5 - больше, но более лёгких воркеров. Меньше конкуренции за I/O от каждого.
  • Мониторинг vacuum-лагов теперь обязателен. Смотрим на pg_stat_user_tables.n_dead_tup и last_autovacuum в алертах - на PG17 это было опционально, на PG18 с io_uring лучше иметь явный порог.

Мы добавили это в memory/lessons.md - потому что без мониторинга можно было обнаружить проблему значительно позже, когда мёртвые кортежи начали бы раздувать таблицы.

Что в итоге

Три месяца в продакшне дали понимание, которого не было на стендах. Инкрементальные чекпоинты - это реальный операционный выигрыш, который не требует ничего кроме правильной конфигурации. Async I/O работает, но его эффект зависит от профиля нагрузки и железа - не ждите магии на умеренных нагрузках.

VACUUM с io_uring - это зона, за которой надо следить. Не критично, но требует настройки и мониторинга. Мы скорректировали конфигурацию и теперь ситуация стабильная.

Следующий шаг - второй контур, аналитический профиль. Там основной интерес в улучшениях планировщика для параллельных запросов - о результатах напишем.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.