PostgreSQL 18 Beta: тестируем async I/O на реальной OLTP-нагрузке и смотрим на цифры
Подняли тестовый стенд на PostgreSQL 18 beta: первые замеры async I/O на OLTP-нагрузках дают +15-20% по throughput. Делимся методикой и наблюдениями.
PostgreSQL 18 Beta 1 - выход первой бета-версии с async I/O и улучшениями parallel query, июль 2025
PostgreSQL Global Development Group выпустила первую бета PostgreSQL 18. Мы не большие любители тестировать беты на production, но несколько раз в год всё-таки поднимаем тестовый стенд на свежем релизе - чтобы не ловить сюрпризы в момент перехода, когда заказчики начнут спрашивать «а мы готовы?». С 18-й решили не тянуть.
Что в этом релизе интересно
Из двух главных вещей, которые гнали нас на стенд, - async I/O и улучшения parallel query.
Async I/O - долгожданная переработка ввода-вывода на уровне ядра Postgres. До 18-й все операции чтения/записи в shared buffers шли синхронно: воркер ждал диска, тред блокировался. Теперь Postgres умеет выдавать несколько I/O-запросов параллельно и продолжать работу пока они не завершились. Это не просто оптимизация конкретного запроса - это изменение в том, как сервер работает с железом под нагрузкой.
Parallel query в 18-й получил ещё несколько улучшений: расширилось число планов, которые могут идти параллельно, появились доработки в cost estimation для параллельных hash join. Для нас это менее горячая тема, чем async I/O, - на OLTP с короткими транзакциями параллельные запросы редко включаются.
Как мы поднимали стенд
Стенд - отдельная виртуальная машина, 16 vCPU, 64 ГБ RAM, NVMe-диск. Не iron-replica production, но достаточно близко к тому, на чём живёт несколько наших клиентов. Установили postgresql18beta1 из официального PGDG-репозитория, рядом поставили 17.4 для контрольного сравнения.
Нагрузку имитировали pgbench с кастомными скриптами под OLTP-профиль: смесь коротких SELECT с point lookup по индексу, UPDATE одной строки, INSERT с sequence. Scale factor подобрали так, чтобы рабочий набор данных не помещался целиком в shared_buffers - иначе диск не задействован и async I/O измерять бессмысленно.
Параметры гнали в двух конфигурациях:
- Умеренная нагрузка - 32 клиента, примерно 60% от насыщения сервера.
- Высокая нагрузка - 128 клиентов, сервер в зоне давления по I/O.
Для async I/O в PG18 ключевой параметр io_method. По умолчанию - worker, это фоновые воркеры для асинхронных операций. Есть ещё io_uring для Linux с поддержкой (у нас ядро 6.1, работает). Гоняли оба варианта.
Что намерили
На умеренной нагрузке разница между PG17 и PG18 небольшая - в пределах погрешности, иногда PG18 чуть быстрее, иногда нет. Это ожидаемо: при 32 клиентах диск не является бутылочным горлом, async I/O просто не раскрывается.
На высокой нагрузке картина интереснее. С io_method=worker throughput (транзакций в секунду) вырос примерно на 15-17% по сравнению с PG17 при тех же условиях. С io_method=io_uring - около 18-20%. Latency при этом снизилась заметно больше в хвостах: p99 улучшился сильнее, чем median, - это видно в гистограмме pgbench. Очевидно, что именно I/O-ожидание давало самые длинные хвосты, и async I/O его срезает.
Параллельно посмотрели на pg_stat_io - новый системный вид, появившийся ещё в PG16 и расширенный в 18-й. Там видно читаемые блоки, хиты, операции read/write по backend-типу. При io_uring количество ожиданий на I/O (io_time в pg_stat_activity) у воркеров упало ощутимо - сервер реально меньше торчит в ожидании диска.
Что стоит учитывать
Несколько наблюдений на полях:
io_uringне везде одинаков. На нашем стенде с ядром 6.1 работает, но мы знаем среды с более старыми ядрами или ограничениями seccomp, гдеio_uringотключён системно. В таком случаеworker- разумный дефолт, тоже даёт прирост, пусть и чуть меньше.- Shared_buffers по-прежнему важен. Async I/O помогает тогда, когда данные реально читаются с диска. Если рабочий набор умещается в кэш - прироста почти нет. Настройка памяти никуда не делась из уравнения.
- Это бета. Мы видели один необъяснённый краш при стрессовом тесте с
io_uringи высоким concurrency - воспроизвести стабильно не смогли, сообщили в трекер. Для production использования пока рано, но это бета и так и задумано. - Параметр
max_io_concurrencyпоявился в 18-й для ограничения числа параллельных I/O-операций на клиента. По умолчанию 16, на быстрых NVMe можно поднять. Мы попробовали 32 - небольшой дополнительный прирост на нашем стенде, но тут нужно смотреть по железу.
Где мы сейчас
Стенд работает, прогоняем дополнительные сценарии - хотим посмотреть на поведение с WAL под нагрузкой и на maintenance операции (VACUUM, ANALYZE) с async I/O. Результаты по этим сценариям будут позже.
Для заказчиков, которые уже используют PG17 и думают о следующем шаге: 18-я выглядит интересно именно для нагрузок, где диск является ограничением. Если у вас OLTP с активной записью и рабочий набор не влезает в RAM - есть смысл поднять свой тестовый стенд на beta и посмотреть на свои цифры. Методику описали, воспроизвести несложно.