Выберите продукт

Linux 6.x: io_uring vs libaio vs POSIX AIO — практическое сравнение для VDS

Разбираем три подхода к async I/O в Linux 6.x: io_uring, libaio и POSIX AIO. Что влияет на latency, throughput и IOPS на NVMe, как честно мерить fio и что выбрать на VDS для БД и веб.
Linux 6.x: io_uring vs libaio vs POSIX AIO — практическое сравнение для VDS

В Linux тема asynchronous I/O давно перестала быть «нишевой». На VDS с NVMe вы быстро упираетесь не только в пропускную способность, но и в latency (задержку I/O), а следом — в IOPS и эффективность CPU. И тут выясняется, что «асинхронщина» в Linux — это не одна технология, а минимум три семейства: POSIX AIO, libaio (Linux native AIO) и io_uring.

Ниже — обзор-практикум для админов и DevOps: чем отличаются подходы, где подвохи в тестах fio, как интерпретировать сравнение производительности и что выбирать в реальных сценариях на VDS.

Что мы сравниваем: API, реализация и «путь» запроса

У всех трёх вариантов общая цель: отправить запрос чтения/записи так, чтобы поток приложения не блокировался, а завершение операции пришло позже «событием». Но в Linux «асинхронность» может достигаться разными механизмами, и это критично для задержек, CPU и хвостов p99.

POSIX AIO: стандартный интерфейс, но не всегда «настоящий» async

POSIX AIO — семейство функций вроде aio_read/aio_write/aio_error/aio_return и уведомления через сигнал или поток. В Linux исторически частый сценарий — реализация через пул потоков в userspace (glibc): запрос фактически выполняется синхронным pread/pwrite, просто «в другом треде».

Что это означает на практике:

  • асинхронность есть, но цена — контекстные переключения и работа планировщика;
  • latency может ухудшаться под нагрузкой, особенно на маленьких блоках;
  • на быстрых NVMe разница между «ядро делает» и «потоки делают» начинает проявляться сильнее.

Плюс POSIX AIO — переносимость и стандартный API. Иногда это решающий фактор, если вы живёте в мире нескольких ОС/платформ.

libaio (Linux native AIO): ближе к ядру, но со своими рамками

libaio — пользовательская библиотека к Linux AIO (интерфейсы io_submit/io_getevents). Исторически это был «родной» асинхронный путь в ядро для файловых дескрипторов, но с важными оговорками по режимам и типам операций.

Классический нюанс: долгое время Linux AIO был наиболее предсказуем и эффективен в сценариях direct I/O (O_DIRECT). Часть «обычного» буферизованного I/O могла вести себя не так, как ожидают от «универсального async». Поэтому многие проекты использовали libaio точечно: под бенчмарки, под прямой ввод-вывод, под специфичные storage-слои.

Сильные стороны libaio:

  • меньше лишних потоков, ближе к ядру;
  • хорошо ложится на очереди и пакетирование запросов;
  • удобен как базовый «референс» для тестов на Linux.

Ограничения:

  • историческая «неровность» по сценариям и файловым режимам;
  • модель событий и композиция операций обычно менее гибкие, чем у io_uring.

io_uring: кольца очередей, меньше syscalls, больше возможностей

io_uring — интерфейс, который в Linux 6.x часто рассматривают как основной кандидат на роль универсального async I/O. Приложение и ядро разделяют две кольцевые очереди (submission/completion): приложение кладёт запросы, ядро возвращает завершения. Во многих режимах это позволяет заметно сократить число системных вызовов и оверхед переходов userspace↔kernel.

Почему это важно админам:

  • при высоких IOPS стоимость syscalls и пробуждений становится сопоставимой со временем самого I/O;
  • io_uring часто улучшает p95/p99 и/или снижает CPU при той же нагрузке;
  • функционально io_uring шире (не только файловое I/O), что помогает event-loop архитектурам.

Почему Linux 6.x и NVMe меняют картину

На медленных дисках различия между API часто «съедаются» ожиданием устройства. На NVMe (что типично для современных VDS) задержка устройства мала, и на первый план выходят:

  • стоимость системного вызова и пробуждений потоков;
  • конкуренция за CPU и runqueue;
  • очереди блочного слоя и поведение планировщика I/O;
  • эффективность пакетирования запросов.

Поэтому сравнение «по одной цифре IOPS» часто вводит в заблуждение: можно получить больше IOPS, но хуже p99 latency — и тогда база/приложение будут медленнее именно в пиках.

Главная продовая метрика — не максимум IOPS, а стабильность задержек (p95/p99) при той глубине очереди и конкуренции, с которой реально живёт ваш сервис.

График p95/p99 задержек при разных iodepth для io_uring, libaio и POSIX AIO

Как корректно сравнивать: fio, параметры и типовые ловушки

Для воспроизводимого сравнения чаще всего берут fio. Но в fio очень легко «случайно» протестировать не то, что вы думаете. Ниже — набор правил, которые делают сравнение io_uring vs libaio vs POSIX AIO более честным.

1) Зафиксируйте семантику: buffered vs direct

Если вы тестируете файловые нагрузки приложения (веб, кэш, генерация статики) — часто важен buffered I/O (страничный кэш). Если тестируете БД и хотите приблизиться к её I/O-пути — чаще нужен direct I/O.

В fio за direct отвечает direct=1 (O_DIRECT). Смешивать режимы при сравнении нельзя: результаты будут про разные подсистемы ядра.

2) Одинаковые iodepth и параллелизм

Async I/O раскрывается при ненулевой глубине очереди, но «правильная» iodepth зависит от workload:

  • OLTP ближе к iodepth 1–16 на поток, важны хвосты p99;
  • стриминг, бэкапы, обработка больших файлов — iodepth 32–256 и выше, важен throughput.

Если поставить iodepth=256 и сделать вывод «io_uring быстрее», вы можете просто протестировать сценарий, который у вашего приложения никогда не случается.

3) Контролируйте CPU: I/O может стать CPU-bound

На NVMe бывает парадокс: устройство «готово», а приложение упирается в CPU на обработке завершений. Поэтому в отчёте держите в фокусе не только IOPS/MB/s, но и:

  • загрузку CPU и распределение по ядрам;
  • контекстные переключения (cs);
  • softirq (актуально для сетевых FS и storage over network);
  • профиль задержек: min/avg/max и percentiles.

Если видите, что у вас «выросли IOPS», но CPU стал 100% и p99 ухудшился, — это важнее любых «красивых» чисел.

4) Прогрев и повторяемость

Делайте прогрев (ramp_time) и достаточную длительность (runtime), иначе получите цифры «на холодном кэше» или словите флуктуации из-за фоновых процессов и writeback. Для сравнения технологий важнее повторяемость, чем абсолютный рекорд.

Шаблон fio-профиля: запуск в трёх ioengine

Ниже — минимальный профиль, который удобно прогонять в трёх вариантах, меняя только ioengine: io_uring, libaio, posixaio. Тестируйте на отдельном файле/разделе, не на боевой FS с данными.

[global]
ioengine=io_uring
thread=1
direct=1
filename=/mnt/testfile
size=8G
time_based=1
runtime=60
ramp_time=10
group_reporting=1

[randread_4k_qd1]
bs=4k
rw=randread
iodepth=1
numjobs=1

[randread_4k_qd32]
bs=4k
rw=randread
iodepth=32
numjobs=1

[randwrite_4k_qd32]
bs=4k
rw=randwrite
iodepth=32
numjobs=1

[seqread_1m_qd32]
bs=1m
rw=read
iodepth=32
numjobs=1

Дальше меняйте только ioengine. Если добавляете специфичные опции io_uring, фиксируйте их явно в отдельном варианте, иначе вы сравните «тюнингованный io_uring» с дефолтным libaio.

Для расширенной диагностики полезно параллельно смотреть реальную картину по диску и очередям: как читать iostat/iotop и параметры fio с учётом I/O scheduler.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Что обычно видно по метрикам: latency, throughput, IOPS

Универсального «io_uring всегда быстрее» нет. Но на Linux 6.x и NVMe типовая картина выглядит так.

Мелкие случайные чтения 4k (randread): борьба за хвосты

При небольшой глубине (iodepth 1–4) IOPS могут быть близкими между движками, а разница проявится в хвостах (p95/p99). io_uring нередко даёт более ровный профиль при росте конкуренции, потому что уменьшает лишние переходы ядро/userspace и пробуждения.

POSIX AIO здесь может проседать из-за потоковой модели: растёт очередь runnable-потоков, и completion начинает задерживаться планировщиком.

Мелкие случайные записи 4k (randwrite): важны flush, fsync и семантика теста

Записи сильно зависят от того, что именно вы измеряете: подтверждённую запись на устройство или запись в кэш. На direct I/O при высокой iodepth io_uring и libaio обычно выглядят сильнее POSIX AIO.

Но для баз данных решают не «fio randwrite», а поведение с fsync, барьерами и журналированием. Поэтому fio — это индикатор потолка и накладных расходов, а не гарантированный ответ «БД будет быстрее на X%».

Последовательные операции большими блоками: потолок по устройству и экономия CPU

На seqread/seqwrite большими блоками вы часто упрётесь в лимит throughput устройства/виртуализации. Различия по MB/s могут быть небольшими, но заметными по CPU: io_uring иногда даёт тот же throughput дешевле по процессору.

Схема влияния виртуализации на I/O: virtio очереди, лимиты и бэкенд NVMe

Что выбрать на VDS: ориентиры по сценариям

Выбор обычно определяется не «модностью», а поддержкой в вашем ПО и реальным I/O-профилем.

Если ваш стек уже умеет io_uring

В Linux 6.x это часто лучший первый выбор, особенно если важны низкая latency и высокая плотность IOPS на NVMe. Практически это означает: при той же нагрузке можно получить либо более ровные хвосты, либо меньше CPU, либо оба эффекта.

Проверьте три вещи:

  • реально ли включён io_uring в приложении (а не просто «собрано с поддержкой»);
  • какой режим I/O используется (direct/buffered);
  • не ограничивает ли вас среда (контейнер, sandbox, версия ядра на хосте, лимиты).

Если нужен предсказуемый «классический» режим, а io_uring недоступен

libaio остаётся рабочей лошадкой, особенно если приложение проектировалось под Linux-native AIO и direct I/O. Важно помнить: часть сценариев «обычного» буферизованного файлового I/O может не давать ожидаемого выигрыша — и это не «ошибка бенчмарка», а особенности пути данных.

Если важна переносимость и нагрузка умеренная

POSIX AIO может быть приемлемым вариантом там, где пиковые IOPS невысоки, а проще опираться на стандартный API. Но если критичны хвосты latency и вы действительно нагружаете диск — POSIX AIO в Linux часто оказывается слабее из-за потоковой реализации.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Практический чек-лист: как сравнить без самообмана

  1. Зафиксируйте среду: одно и то же ядро, один и тот же тип диска/тома, одинаковая FS и параметры монтирования.

  2. Опишите workload: блок (4k/16k/128k/1m), rw (rand/seq), доля read/write, целевая iodepth и numjobs.

  3. Запустите fio в трёх вариантах ioengine, меняя только движок.

  4. Смотрите percentiles latency (p95/p99) и CPU, а не только средние значения.

  5. Сделайте 3–5 прогонов и сравните разброс. Если разброс большой — ищите фоновые причины: noisy neighbor, writeback, steal time, лимиты по IOPS/throughput.

Нюансы виртуализации: почему на VDS результат отличается от «железа»

На VDS вы измеряете не только подсистему Linux, но и слой виртуализации/хранилища провайдера. На результат влияют:

  • тип виртуального диска (virtio-blk, virtio-scsi, NVMe emulation);
  • настройки очередей и multi-queue;
  • политики шеринга NVMe и лимиты по IOPS/throughput;
  • CPU steal и конкуренция на хосте (особенно заметно в хвостах latency).

Правильный админский подход: сначала понять, где вы на кривой «disk-bound vs cpu-bound», а затем уже делать выводы про io_uring/libaio/POSIX AIO и подбирать глубину очереди.

Итоги

В Linux 6.x io_uring чаще всего даёт самый современный и эффективный путь для async I/O, особенно на NVMe и при высоких IOPS, где критичны latency и стоимость syscalls. libaio остаётся актуальным в Linux-native сценариях и полезен как «понятная база» для тестов и некоторых storage-режимов. POSIX AIO в Linux нередко является компромиссом: удобный стандартный интерфейс, но потенциально более высокий оверхед из-за потоковой реализации.

Если вы выбираете технологию под конкретный сервис на VDS: начните с честного fio-профиля под ваш workload, сравните p99 и CPU, проверьте повторяемость — и только потом решайте, что включать в прод.

Поделиться статьей

Вам будет интересно

S3-compatible vs WebDAV vs SFTP для backup storage: что выбрать и почему OpenAI Статья написана AI (GPT 5)

S3-compatible vs WebDAV vs SFTP для backup storage: что выбрать и почему

Когда бэкапы нужно хранить вне сервера, выбор обычно сводится к S3-compatible объектному хранилищу, WebDAV или SFTP. Разбираю, что ...
DNS-записи CAA, TLSA (DANE), HTTPS/SVCB и ALIAS/ANAME: зачем нужны и как применять OpenAI Статья написана AI (GPT 5)

DNS-записи CAA, TLSA (DANE), HTTPS/SVCB и ALIAS/ANAME: зачем нужны и как применять

CAA ограничивает выпуск TLS-сертификатов, TLSA (DANE) связывает TLS с DNSSEC (чаще всего для SMTP), HTTPS/SVCB помогает клиентам в ...
SSL API: mTLS vs API keys vs OAuth2 client credentials OpenAI Статья написана AI (GPT 5)

SSL API: mTLS vs API keys vs OAuth2 client credentials

Практический разбор трёх подходов к межсервисной аутентификации в API: mTLS, API keys и OAuth2 client credentials. Разбираем threa ...