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

Установка и первый замер

В Ubuntu и Linux Mint пакет лежит в обычном репозитории, собирается стабильно и не требует сторонних источников. Для io_uring нужен fio 3.2 и новее, это легко проверить по версии.

sudo apt install fio
fio --version
fio --name=seqread --filename=/srv/testfile.bin --rw=read --bs=1M \
    --size=1G --ioengine=io_uring --direct=1 --iodepth=16 \
    --runtime=60 --time_based --output-format=json --output=seqread.json

Параметр direct=1 отключает буфер страниц: без него вы измеряете скорость памяти, а не диска. io_uring предпочтительнее libaio, который считается устаревшим; на ядрах до 5.1 придётся вернуться к libaio или psync. Важно: указание filename=/dev/nvme0n1 перезаписывает содержимое устройства, поэтому для безопасных тестов используйте обычный файл на отдельном разделе или каталог через —directory.

Профили под реальную нагрузку

Синтетические мегабайты в секунду редко совпадают с реальностью. СУБД, веб-сервер и бэкап читают диск по-разному, и один тест этого не покажет.

  • база данных: случайное чтение блоками 4K с глубиной очереди 32 — базовый профиль;
  • смешанная нагрузка веб-приложения: randrw с rwmixread=70, блоки 8K, глубина 8–16;
  • журнал транзакций: последовательная запись с fsync показывает реальную скорость коммита.
fio --name=db --filename=/srv/testfile.bin --rw=randread --bs=4k \
    --iodepth=32 --ioengine=io_uring --direct=1 --numjobs=4 \
    --runtime=120 --time_based --group_reporting
fio --name=wal --filename=/srv/testfile.bin --rw=write --bs=4k \
    --fsync=32 --ioengine=io_uring --direct=1 --runtime=60

numjobs запускает несколько параллельных потоков, а group_reporting сводит их в один отчёт. fsync заставляет fio синхронизировать запись каждые 32 операции: без этого данные уйдут в кэш, и вы получите красивые цифры, не имеющие отношения к производительности сервера.

Читать не среднее, а перцентили

В выводе есть IOPS, мегабайты и латентность, но средняя латентность обманчива: машина может отдавать сто тысяч IOPS при средних 200 микросекунд и при этом раз в минуту подвешиваться на секунду. Именно эта секунда становится жалобами пользователей.

  • поля clat percentiles показывают 99-й и 99.9-й уровни, по ним и судят о просадках;
  • флаг —percentile_list=50:99:99.9:99.99 добавляет нужные уровни в отчёт;
  • slat отражает ожидание планировщика, а clat — время в очереди ядра, это разные диагнозы;
  • сравнивать можно только одинаковые профили: последовательное чтение с 1M не сравнивается с randread 4k.

I/O планировщик: что выбрать

Планировщик решает, в каком порядке запросы уходят в устройство. Посмотрите, что доступно именно на вашем диске.

cat /sys/block/sda/queue/scheduler
cat /sys/block/nvme0n1/queue/scheduler
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler

В выводе активный планировщик отмечен квадратными скобками. Типичная картина: для жёстких дисков bfq или mq-deadline, для SATA-SSD mq-deadline, для NVMe — none, поскольку у быстрого устройства собственная очередь эффективнее пользовательской дисциплины. Команда выше меняет планировщик на лету, но изменение не переживает перезагрузку. Постоянное правило задаётся udev-правилом в каталоге /etc/udev/rules.d.

ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="mq-deadline"
ACTION=="add|change", KERNEL=="nvme[0-9]n[0-9]", ATTR{queue/scheduler}="none"

Отдельно стоит упомянуть десктоп: bfq на рабочей станции с мелкими записями браузера часто ощущается отзывчивее, чем none, хотя в бенчмарках выигрыш минимален. На сервере с несколькими десятками потоков картина обратная: лишняя обработка запросов планировщиком увеличивает латентность. Поэтому настройки для десктопа и для базы данных — это два разных профиля, и переносить их на другой класс машин без повторных замеров не стоит.

Разницу проверяйте на своих профилях fio, а не по рекомендациям из сети: на NVMe переключение между none и mq-deadline часто даёт меньше трёх процентов, тогда как на HDD с мелкими случайными чтениями планировщики различаются в разы.

Что реально важно помимо планировщика

  • read_ahead_kb — предвыборка: для последовательных задач 128–512, для случайных 4–16;
  • nr_requests — размер очереди, на глубоких профилях её рост снижает латентность;
  • частота CPU: governor performance против schedutil даёт до 10 процентов на случайных операциях;
  • iostat -x 1 показывает util, await и iowait, им ловят фоновую нагрузку до теста;
  • отключите swap и убедитесь, что в фоне не крутится обновление пакетов — иначе результат исказится.

Как сравнивать результаты между дисками

Один бенчмарк ничего не доказывает: сравнимый замер должен повторять условия — тот же job-файл, тот же размер файла, та же длительность и одинаковое окружение. Удобно вынести параметры в отдельный файл и хранить его рядом со скриптами.

[db]
rw=randread
bs=4k
size=4G
direct=1
ioengine=io_uring
iodepth=32
numjobs=4
group_reporting=1
runtime=120
time_based=1

Запуск становится короче: fio db.fio —filename=/srv/testfile.bin —output-format=json > db.json. Дальше отчёт легко разбирать скриптом, например jq вытащит средние IOPS или перцентили латентности. Практика показывает, что коллекция таких JSON-файлов за полгода окупается при переезде на новый сервер: вы сразу видите, ускорился накопитель или изменилась нагрузка, а не гадаете по субъективным ощущениям.

Типичные проблемы при тестировании

Практика показывает одни и те же грабли. Ошибка «Operation not supported» при direct=1 означает, что файловая система или драйвер не умеют прямой доступ — переходите на direct=0 и помните про кэш. Тест на занятой файловой системе засоряют чужие записи, поэтому берите отдельный раздел. Новый SSD сразу после TRIM показывает завышенные цифры: прогоните подготовку записью полного объёма. И наконец, если файл меньше кэша памяти, замер отражает память, а не накопитель. Готовые сценарии удобно оформлять в job-файлы и запускать через fio db.fio —output-format=json, а обогащать вывод метриками — через jq.

Итог

Нет «лучшего планировщика» и нет «эталонной команды fio»: есть ваша нагрузка, корректно воспроизведённая профилем, честный прямой доступ к накопителю и перцентили вместо средних значений. Сначала измеряйте baseline одним и тем же профилем, потом меняйте по одному параметру — планировщик, глубину очереди, предвыборку — и сохраняйте JSON-отчёты, чтобы через полгода сравнить диски после обновления прошивки или переезда на другой сервер.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте