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 - это зона, за которой надо следить. Не критично, но требует настройки и мониторинга. Мы скорректировали конфигурацию и теперь ситуация стабильная.
Следующий шаг - второй контур, аналитический профиль. Там основной интерес в улучшениях планировщика для параллельных запросов - о результатах напишем.