PostgreSQL 18 RC: гоняем нагрузочные тесты на реальных схемах и оцениваем production-готовность
Прогнали нагрузочные тесты PostgreSQL 18 RC на схемах реальных клиентов: async I/O работает, разбираем конфигурацию и известные регрессии перед GA.
PostgreSQL 18 Release Candidate - оценка production-готовности перед GA, октябрь 2025
В июле мы гоняли PostgreSQL 18 beta на тестовом стенде и смотрели на async I/O в принципе. С тех пор вышел Release Candidate, и подход к тестированию изменился: теперь у нас есть реальные схемы клиентов, снятые копии данных и конкретные вопросы от заказчиков - «вы рекомендуете переходить сразу после GA или подождать пару патчей?».
Коротко: RC - это уже не бета. Но нюансов хватает.
Как мы тестировали на этот раз
Три клиентских профиля, три разных характера нагрузки:
- Профиль A - финтех OLTP. Высокая конкурентность, короткие транзакции, активная запись. Рабочий набор не помещается в shared_buffers - именно тот случай, где async I/O должен раскрываться.
- Профиль B - аналитика с batch-обработкой. Ночные ETL-джобы, параллельные hash join по большим таблицам, сложные оконные функции.
- Профиль C - смешанный. Много одновременных коротких SELECT для web-бэкенда плюс периодические тяжёлые отчёты. Клиент сидит на connection pool через PgBouncer.
Данные - реальные, но обезличенные копии, поднятые на выделенных виртуальных машинах, изолированных от production. PostgreSQL 18 RC1 из PGDG-репозитория, рядом для контроля 17.4.
Async I/O: теперь это не эксперимент
На профиле A разница с PG17 заметна невооружённым глазом уже в первые минуты нагрузочного прогона. При io_method=io_uring (ядро 6.1, io_uring не заблокирован) throughput вырос относительно PG17 примерно на те же 18-20%, что мы видели на синтетике. Важнее другое: хвосты latency ведут себя значительно стабильнее. В бете у нас бывали всплески p99, которые портили картину. В RC этого нет - поведение ровное под стабильной нагрузкой.
Конфигурация, которая сложилась у нас для профиля A:
io_method = io_uring- если ядро позволяет, иначеworkermax_io_concurrency = 32- подняли с дефолтных 16; на быстром NVMe даёт небольшой дополнительный приростshared_buffers- оставили стандартный подход (25% RAM), async I/O не меняет эту рекомендациюeffective_io_concurrency- в 18-й его роль изменилась, теперь это hint для планировщика о возможностях железа, а не ограничитель; для NVMe ставим 200+
На профиле B прирост от async I/O скромнее - параллельные запросы используют несколько рабочих процессов, и конкуренция за I/O там другая. Зато улучшения в parallel query cost estimation дают о себе знать: несколько тяжёлых аналитических запросов получили лучший план и ускорились без каких-либо подсказок.
Регрессии, которые мы нашли
Честно говоря, RC - это RC, и мы шли с ожиданием что-нибудь найти. Нашли две вещи.
Первое - специфика PgBouncer + RC. На профиле C с PgBouncer в transaction mode поймали редкое, но воспроизводимое зависание при высоком числе одновременных сессий. Покопались - оказалось, что в RC изменилось поведение одного из протокольных сообщений в конкретной комбинации с версией PgBouncer 1.21. Обновление PgBouncer до 1.23 проблему устранило. Это не баг Postgres как таковой, скорее совместимость - но для команд, где PgBouncer критичен, надо проверить версию заранее.
Второе - pg_stat_statements и некоторые DDL-запросы. В паре случаев нормализация текста запроса в pg_stat_statements давала неожиданный результат для CREATE TABLE ... LIKE с несколькими параметрами - запросы попадали в разные строки, хотя по логике должны были быть сгруппированы. Это влияет на инструменты мониторинга, которые строят агрегаты по pg_stat_statements. Пока работаем с этим через ручную фильтрацию в наших дашбордах, баг уже есть в трекере PG.
Других значимых регрессий не нашли - и это хорошая новость для RC.
Что с миграцией на практике
Для типичного перехода с PG17 на PG18 pg_upgrade работает штатно - проверили на всех трёх профилях. Время апгрейда сопоставимо с предыдущими мажорными переходами.
Отдельно стоит пройтись по конфигурации после апгрейда:
io_method- новый параметр, в существующих конфигах его нет. Дефолтworkerработает корректно, но стоит осознанно выбрать значение под своё железо и ядро.effective_io_concurrency- если у вас было выставлено значение под старую семантику, его стоит пересмотреть.- Расширения - некоторые сторонние расширения ещё не пересобраны под PG18 RC. Инвентаризация расширений перед апгрейдом обязательна.
Где мы сейчас
RC в production не шёл - это очевидно и не обсуждалось. GA вышел 25 сентября, и ответ на вопрос заказчиков «переходить сразу или ждать?» у нас теперь конкретный: для нагрузок с реальным I/O-давлением (профиль A) - переходить, после проверки совместимости расширений и версии PgBouncer. Прирост реальный, RC вёл себя стабильно, регрессии локализованные и устранимые.
Для аналитических нагрузок (профиль B) - тоже интересно, но менее срочно. Там основной выигрыш дают изменения в планировщике, а не async I/O, и это требует отдельного тюнинга под конкретные запросы.
GA вышел 25 сентября - уже после того, как мы завершили тестирование RC. Ждём первых патч-нот от PG-команды; если появятся новые наблюдения по конкретным конфигурациям - напишем.