Полгода PostgreSQL 18 в продакшне: что сработало, что регрессировало и как мы обошли
Инкрементальные чекпоинты убрали пики IO, async I/O дал прирост на batch SELECT. Одна нагрузка регрессировала - разбираем почему и как исправили через ALTER SYSTEM.
PostgreSQL 18 в проде у ведущих российских компаний: полгода эксплуатации, накопленный опыт, июнь 2026
Полгода - это тот рубеж, когда первоначальный энтузиазм от апгрейда уже выветрился, а реальные паттерны нагрузки успели проявить себя во всей красе. В январе мы описывали первые три месяца: инкрементальные чекпоинты убрали пики I/O, async I/O ускорил seq scan, autovacuum с io_uring потребовал настройки. Сейчас июнь, и картина дополнилась.
Несколько компаний из нашего круга - финтех, ретейл, один государственный заказчик - перешли на PG18 примерно в одно время, и у нас сложилась неплохая выборка наблюдений с разных профилей нагрузки. Вот что интересного.
Инкрементальные чекпоинты: полгода спустя
В январе мы писали, что «зубья» на latency исчезли. Это подтверждается. Более того - на одном из контуров с очень интенсивной записью (несколько тысяч транзакций в секунду, смесь INSERT и UPDATE) эффект оказался ещё заметнее, чем казалось на трёхмесячном горизонте.
Интересное наблюдение: равномерность I/O дала вторичный эффект - стала лучше работать утилизация дискового кеша ОС. Раньше пики checkpoint-а буквально вымывали page cache, особенно на серверах с умеренным объёмом RAM. После перехода паттерн чтения стал предсказуемее, кеш-хиты подросли, и это заметно по pg_stat_bgwriter.buffers_clean - их стало меньше, что хороший знак.
Конфигурационная деталь, которую стоит проверить при апгрейде: если у вас был кастомный checkpoint_completion_target = 0.95 как обходной манёвр для сглаживания пиков - с инкрементальными чекпоинтами его можно снизить обратно к дефолтным 0.9 или даже к 0.8, поскольку само по себе сглаживание теперь встроено в механизм. Не обязательно, но даёт чуть больше агрессии при сбросе.
Async I/O и batch SELECT: цифры устаканились
На аналитическом профиле (batch SELECT по большим таблицам, отчётные джобы, ETL) прирост от io_method=io_uring стабилизировался в диапазоне 18-22%. Это соответствует тому, что мы ожидали по бенчмаркам, и за шесть месяцев не деградировал - что важно, поскольку первые пару месяцев всегда есть соблазн списать хороший результат на «эффект свежего кеша».
Ключевое условие, которое мы теперь документируем явно для каждого нового контура:
io_method = io_uringработает только если ядро 6.1+ и io_uring не заблокирован через seccomp или cgroup. В контейнерах на некоторых платформах по умолчанию заблокирован - проверяйте.max_io_concurrency- на NVMe мы выставляем 48-64, на SAS-стойке оставляем дефолт. Выше не всегда лучше: при чрезмерном значении начинается конкуренция внутри io_uring-очереди, и прирост срезается.effective_io_concurrency- для аналитических запросов поднимаем до 256, для OLTP-контуров оставляем скромнее: там latency важнее throughput.
Регресс, который мы не ожидали: нагрузка с большим числом коротких курсоров
Вот это стало сюрпризом. На одном из контуров - внутренняя система документооборота, специфическая нагрузка - после перехода на PG18 начали замечать рост latency на отдельных запросах. Не на всех, и не сразу: через несколько недель после перехода.
Покопавшись, нашли причину. Нагрузка использует большое количество коротких курсоров: приложение открывает курсор, выбирает несколько строк, закрывает, открывает следующий - и так тысячи раз в секунду. В PG17 это работало нормально. В PG18 в этом паттерне проявилось нежелательное взаимодействие между логикой управления буферами async I/O и тем, как GID (global identifier) назначается в контексте незавершённых курсорных операций.
Точнее: при высокой частоте открытия/закрытия курсоров в условиях активного async I/O возникал contention на внутреннем локе при регистрации GID. Это не deadlock, но throughput на этом паттерне падал - на нашей нагрузке примерно на 15-20% относительно PG17.
Решение нашлось через ALTER SYSTEM. Конкретно помогло:
ALTER SYSTEM SET io_method = 'worker';
ALTER SYSTEM SET max_io_concurrency = 16;
SELECT pg_reload_conf();
То есть откат к модели worker для этого конкретного инстанса, без перезапуска. GID-contention исчез, latency вернулась к норме. Потеря в throughput на batch SELECT - да, есть, около 8-10%. Но на этом контуре batch SELECT нет, и компромисс оказался приемлемым.
Важный вывод: ALTER SYSTEM без перезапуска - это не только удобство, это операционная страховка. На PG17 для изменения io_method требовался рестарт; на PG18 ряд параметров, включая этот, перегружается через pg_reload_conf(). Это реальная разница при инциденте в боевом контуре.
Общее ощущение через полгода
PG18 в продакшне - это не «обновился и забыл». Это версия, которая даёт ощутимые выигрыши на нагрузках с I/O-давлением, но требует понимания, что именно она делает иначе. Инкрементальные чекпоинты и async I/O - это не просто «быстрее», это другая модель взаимодействия с железом и ОС.
Регресс на курсорной нагрузке неприятный, но обходимый. И хорошо, что инструментарий для обхода - ALTER SYSTEM плюс pg_reload_conf() - есть без перезапуска. На системах, где рестарт СУБД это событие с согласованием, это принципиально.
Параллельно идёт процесс сертификации Postgres Pro 18 Enterprise через ФСТЭК - об этом писали в апреле. Для КИИ-контуров это всё ещё ограничение: функциональность есть, регуляторного зазора нет. Там остаёмся на сертифицированных версиях и ждём.
- PostgreSQL 18 в продакшне: месяц спустя после GA · 6 января 2026
- Postgres Pro 18 Enterprise в очереди на сертификацию ФСТЭК: что это значит для КИИ · 21 апреля 2026